ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScriptのモジュール化:カプセル化(クロージャ)、継承(プロトタイプ)入門_JavaScriptスキル

JavaScriptのモジュール化:カプセル化(クロージャ)、継承(プロトタイプ)入門_JavaScriptスキル

WBOY
WBOYオリジナル
2016-05-16 17:28:171051ブラウズ

JavaScript は本質的にカジュアルなものですが、ブラウザーができることが増えるにつれて、この言語はますます本格的になってきています。複雑なロジックでは、JavaScript をモジュール化する必要があり、モジュールをカプセル化して、外部呼び出し用のインターフェイスのみを残す必要があります。クロージャはJavaScriptにおけるモジュールのカプセル化の鍵であり、多くの初心者が理解するのが難しいポイントでもあります。最初は戸惑いました。今では、この概念をより深く理解できるようになったと確信しています。理解を容易にするために、この記事では比較的単純なオブジェクトをカプセル化することを試みます。

ページ上で値 n を維持するカウンタ オブジェクト ティッカーを維持しようとしています。ユーザーが操作すると、カウントを増やす (値 n に 1 を加える) ことはできますが、n を減らしたり、n を直接変更したりすることはできません。さらに、この値を時々クエリする必要があります。

オープンドア JSON スタイルのモジュール性

ドアを開ける方法は次のとおりです:

コードをコピーします コードは次のとおりです:

varticker = {
n:0,
tiny:function(){
this.n ;
},
};

この書き方は自然で効果的です。カウントを増やす必要がある場合は、ticker.tick() メソッドを呼び出し、ticker.n 変数にアクセスします。しかし、その欠点も明らかです。モジュールのユーザーは、ticker.n-- やticker.n=-1 を呼び出すなど、n を自由に変更できます。ティッカーはカプセル化されていません。n とticker() はティッカーの「メンバー」であるように見えますが、それらのアクセシビリティはティッカーと同じであり、グローバルです (ティッカーがグローバル変数の場合)。カプセル化という点では、このモジュール方式のアプローチは、次のアプローチよりも少しだけばかばかしいだけです (ただし、一部の単純なアプリケーションでは、この少しのアプローチで十分です)。

コードをコピー コードは次のとおりです:

varticker = {};
vartickerN = 0;
vartickerTick = function(){
tickerN ;
}

tickerTick();

tick() で this.n にアクセスすることに注意してください。これは、n がticker のメンバーであるためではなく、ticker がticker() を呼び出すためです。実際、ここにticker.nを書く方がよいでしょう。なぜなら、tick()が呼び出された場合、それはティッカーではなく、次のような別の何かであるからです:

コードをコピーします コードは次のとおりです:

var func =ticker.tick;
func( );

この時、実際にtick()を呼び出しているのはwindowであり、関数を実行するとwindow.nにアクセスしようとしてエラーが発生します。

実際、この「オープンドア」モジュラーアプローチは、プログラムではなく JSON スタイルのデータを整理するためによく使用されます。たとえば、次の JSON オブジェクトをティッカーの関数に渡して、ティッカーが 100 からカウントを開始し、毎回 2 ずつ進むことを決定できます。

コードをコピー コードは次のとおりです:

var config = {
nStart: 100、
ステップ:2
}

スコープ チェーンとクロージャ
次のコードを見てください。設定を渡すことでティッカーのカスタマイズが実装されていることに注意してください。

コードをコピー コードは次のとおりです:

functionticker(config){
var n = config.nStart;
function tiny(){
n = config.step;
}
}
console.log(ticker.n); // ->未定義

なぜティッカーがオブジェクトから関数に変わったのか疑問に思われるかもしれません。これは、JavaScript では関数のみがスコープを持ち、関数内の変数には関数本体の外からアクセスできないためです。 ticker() の外部でticker.n にアクセスすると undefine が発生しますが、ticker() 内で n にアクセスすると問題ありません。 Nick() から Ticker()、そしてグローバルまで、これは JavaScript の「スコープ チェーン」です。

しかし、まだ問題があります。それは、tick() をどのように呼び出すかということです。 ticker() のスコープには、ticker() も含まれます。解決策は 2 つあります:

•1)ticker() の戻り値として n をインクリメントするメソッドを使用するのと同じように、戻り値としてメソッドを呼び出す必要があります。
•2) 外側のスコープの変数を設定するだけです。 ticker() で行ったように getN を に設定します。

コードをコピー コードは次のとおりです。

var getN;
functionticker( config){
var n = config.nStart;
getN = function(){
return n;
};
return function(){
n = config.step;
};
}

vartic =ticker({nStart:100,step:2});
tick();
console.log(getN()); // ->102

ご覧のとおり、現時点では変数 n は「クロージャ」内にあり、ticker() の外部から直接アクセスすることはできませんが、2 つのメソッドを通じて監視または操作できます。

このセクションの最初のコードでは、ticker() メソッドが実行された後、次回関数が呼び出されるまで n とticker() が破棄されますが、2 番目のコードでは、ticker() メソッドの実行後に破棄されます。実行後、n は破棄されません。これは、tick() と getN() がそれにアクセスしたり変更したりする可能性があり、ブラウザーが n を維持する責任を負うことになります。 「クロージャ」についての私の理解は、関数のスコープ内の変数である n が関数の実行後に維持される必要があり、他のメソッドを通じてアクセスできることを保証するために使用されるメカニズムです。

でも、まだ何か違う気がするんですが?同じ機能を持つ 2 つのオブジェクトticker1とticker2を維持する必要がある場合はどうすればよいでしょうか? ticker() は 1 つしかないので、再度書くことはできませんね。

new 演算子とコンストラクター
new 演算子を介して関数を呼び出すと、新しいオブジェクトが作成され、そのオブジェクトを使用して関数が呼び出されます。私の理解では、次のコードの t1 と t2 の構築プロセスは同じです。

コードをコピー コードは次のとおりです:

function myClass(){}
var t1 = 新しい myClass();
var t2 = {};
t2.func = myClass;
t2.func();
t2.func = 未定義;

T1 と t2 は両方とも新しく構築されたオブジェクトであり、myClass() がコンストラクターです。同様に、ticker() も書き換えることができます。

コードをコピー コードは次のとおりです:

function TICKER(config){
var n = config.nStart;
this.getN = function(){
return n;
};
this.tick = function(){
n = config.step;
}
}

varticker1 = new TICKER({nStart:100,step:2});
ticker1.tick();
console.log(ticker1.getN()); // ->102
varticker2 = new TICKER({nStart:20,step:3});
ticker2.tick();
ticker2.tick();
console.log(ticker2.getN()) ; // ->26

慣例により、コンストラクターは大文字で表記されます。 TICKER() は依然として関数であり、純粋なオブジェクトではないことに注意してください (「純粋」と言う理由は、関数が実際にはオブジェクトであり、TICKER() が関数オブジェクトであるためです。クロージャはまだ有効であり、ticker1 にアクセスできません)。 .n .

プロトタイプのプロトタイプと継承
上記の TICKER() にはまだ欠陥があります。つまり、ticker1.tick() とticker2.tick() は互いに独立しています。 new 演算子を使用して TICKER() を呼び出すたびに、新しいオブジェクトが生成され、この新しいオブジェクトにバインドする新しい関数が生成されます。新しいオブジェクトが構築されるたびに、ブラウザーが開きます。 Nick() 自体と変数を Tick() 内に保存することは、私たちが期待しているものではありません。 ticker1.tick とticker2.tick が同じ関数オブジェクトを指すことが期待されます。

これにはプロトタイプの導入が必要です。

JavaScript では、Object オブジェクトを除き、他のオブジェクトには別のオブジェクトを指すプロトタイプ プロパティがあります。この「別のオブジェクト」にはまだそのプロトタイプ オブジェクトがあり、最終的に Object オブジェクトを指すプロトタイプ チェーンを形成します。オブジェクトのメソッドを呼び出すときに、そのオブジェクトに指定されたメソッドがないことが判明した場合は、Object オブジェクトが見つかるまでプロトタイプ チェーン上でこのメソッドを検索します。

関数はオブジェクトでもあるため、関数にはプロトタイプ オブジェクトもあります。関数が宣言されると (つまり、関数オブジェクトが定義されると)、新しいオブジェクトが生成され、このオブジェクトのプロトタイプ プロパティは Object オブジェクトを指し、このオブジェクトのコンストラクター プロパティは関数オブジェクトを指します。

コンストラクターを通じて構築された新しいオブジェクトのプロトタイプは、コンストラクターのプロトタイプ オブジェクトを指します。したがって、コンストラクターのプロトタイプ オブジェクトに関数を追加できます。これらの関数は、ticker1 やticker2 ではなく、TICKER に依存します。

次のようにするとよいでしょう:

コードをコピー コードは次のとおりです:

function TICKER(config){
var n = config.nStart;
}
TICKER.prototype.getN = function{
// 注意: 無効な実装
return n;
};
TICKER.prototype.tick = function{
// 注意: 無効な実装
n = config.step;
};

これは無効な実装であることに注意してください。プロトタイプ オブジェクトのメソッドはクロージャの内容、つまり変数 n にアクセスできないためです。 TICK() メソッドが実行されると、n にはアクセスできなくなり、ブラウザーは n を破棄します。クロージャの内容にアクセスするには、オブジェクトには、クロージャの内容にアクセスするためのいくつかの簡潔なインスタンス依存メソッドが必要であり、そのプロトタイプで複雑なパブリック メソッドを定義してロジックを実装する必要があります。実際、例の Nick() メソッドは十分に簡潔なので、それを TICKER に戻しましょう。次に、より複雑なメソッドticTimes()を実装します。これにより、呼び出し元はtick()の呼び出し回数を指定できるようになります。

コードをコピー コードは次のとおりです:

function TICKER(config){
var n = config.nStart;
this.getN = function(){
return n;
};
this.tick = function(){
n = config.step;
} ;
}
TICKER.prototype.tickTimes = function(n){
while(n>0){
this.tick();
n--;
}
};
varticker1 = new TICKER({nStart:100,step:2});
ticker1.tick();
console.log(ticker1.getN()); // - >102
varticker2 = new TICKER({nStart:20,step:3});
ticker2.tickTimes(2);
console.log(ticker2.getN()); // - >26

このティッカーは素晴らしいです。 n はカプセル化されており、オブジェクトの外部から直接変更することはできません。また、複雑な関数 checkTimes() がプロトタイプで定義されており、この関数はインスタンスの小さな関数を呼び出すことによってオブジェクト内のデータを操作します。

そこで、オブジェクトのカプセル化を維持するために、私の提案は、データの操作を可能な限り最小の単位関数に分離し、コンストラクターでインスタンス依存 (" "プライベート" とも呼ばれる) として定義し、プロトタイプ (つまり「パブリック」) に複雑なロジックを実装します。

最後に、継承について話しましょう。実際、プロトタイプで関数を定義するとき、すでに継承を使用しています。 JavaScript の継承は、C よりも...まあ...単純、または粗雑です。 C では、動物を表すために Animal クラスを定義し、鳥を表すために Animal クラスを継承する Bird クラスを定義できますが、私が議論したいのはそのような継承ではありません (ただし、このような継承は JavaScript でも実装できます) C での継承の説明は、動物クラスを定義してから myAnimal オブジェクトをインスタンス化することです。はい、これは C ではインスタンス化ですが、JavaScript では継承として扱われます。

JavaScript はクラスをサポートしていません。ブラウザは現在利用可能なオブジェクトのみを考慮し、これらのオブジェクトがどのようなクラスであるか、どのような構造を持つべきかについては気にしません。この例では、TICKER() は関数オブジェクトであり、値を割り当てたり (TICKER=1)、削除したりすることができます (TICKER=unknown)。呼び出されると、TICKER() はコンストラクターとして機能し、TICKER.prototype オブジェクトはクラスとして機能します。

上記は私が知っているJavaScriptのモジュール化方法です。初心者の方も参考になれば幸いです。何か間違っているところがあれば、ご指摘ください。

著者: Yiyezhai マスター
出典: www.cnblogs.com/yiyezhai

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。