ホームページ > 記事 > ウェブフロントエンド > JavaScript クロージャ - クロージャ内の変数とこのオブジェクト
JavaScript のスコープチェーンのメカニズムは、いくつかの副作用を引き起こす可能性があります。クロージャーは、それを含む関数内の変数の最後の値しか取得できません。クロージャを使用するときは、変数値に注意を払う必要があります。これは、変数値で間違いがよく発生するためです。
以下では、この問題を説明するために非常に極端な例を使用します。実際の開発では、通常、このようなコードは書きません。この例のコードは次のとおりです。
function fn1(){ var arr = new Array(); //变量i保存在fn1作用域中 for(var i = 0; i < 10;i++){ arr[i] = function(){ return i; } } return arr; } var values = fn1(); for(var i = 0; i < values.length;i++){ //此时通过闭包来调用所有的函数,当输出i的时候会到上一级的作用域中查找,此时i的值是10,所以输出的都是10 document.write(values[i]()+"<br>"); }
上記のコードを実行すると、ページに 0 ~ 9 が印刷されると予想されましたが、実際には 10 が 10 個印刷されます。このコードを分析してみましょう。実装コードで関数 fn1 が作成され、関数で配列オブジェクトが作成され、for ループを通じて配列に値が割り当てられます。ループは 10 回繰り返され、毎回無名関数が実行されます。を配列に代入して値を返し、最後に配列オブジェクトを返します。次に、fn1 関数への参照を取得し、ループを通じてページ上の配列の値を出力します。
上記のプログラムのスコープ チェーン メモリ モデルを以下の図に示します。
この図から、 fn1 関数の各ループが無名関数を生成し、独自のスコープ チェーンを持っていることがわかります。スコープ チェーンの上位ビットはグローバル スコープを指し、中間ビットは外側の fn1 スコープを指し、下位ビットは独自のスコープを指します。
関数 fn1 の実行が完了すると、fn1 のスコープ内の属性 i の値は 10 になります。このとき、GC は fn1 のリサイクルを開始しますが、fn1 のスコープを指す匿名関数が存在することがわかります。したがって、fn1 のスコープはリサイクルされません。
匿名関数が実行されると、独自の空間で属性 i を検索しますが、見つからないため、上位の fn1 スコープに移動して検索します。このとき、fn1 スコープの i 値は 10 です。したがって、匿名関数はすべて同じ i 値 10 を取得します。
この問題を解決する方法は、匿名関数で匿名関数を返し、変数を通じて現在の値を保存することです。コードは次のとおりです。
function fn1(){ var arr = new Array(); for(var i = 0; i < 10;i++){ arr[i] = function(num){ return function(){ return num; } }(i); } return arr; } var values = fn1(); for(var i = 0; i < values.length;i++){ //每一个fs都是在不同的作用域链中,num也是保存在不同的作用域中,所以输出0-9 document.write(values[i]()+"<br>"); }
このとき、num の値は各匿名関数のスコープに格納され、その値は各ループのインデックス値とまったく同じになります。このように、匿名関数が呼び出されるたびに、独自の空間で num 属性が検索され、これらの num の値は異なりますが、同時に i 属性は検索されません。 fn1 関数のスコープ。
上記のコードは 20 個の匿名関数のスコープを生成します。コードが単純な戻り値ではなく、より複雑な演算である場合、多くのメモリ領域を占有します。
クロージャ内のこのオブジェクト
このオブジェクトをクロージャ内で使用すると、予期しない問題も発生します。 this オブジェクトは、関数の実行環境に基づいて実行時にバインドされます。グローバル関数の場合、このオブジェクトはウィンドウであり、関数がオブジェクトのメソッドとして呼び出される場合、このオブジェクトはそのオブジェクトです。匿名関数では、このオブジェクトは通常ウィンドウを指します。次の例を見てみましょう:
var name = "window"; var person = { name : "Leon", age:22, say:function(){ return function(){ return this.name; } } } console.info(person.say()()); //控制台输出:window
上記のコードでは、person オブジェクトのsay() メソッドを呼び出すと、出力されるのは person オブジェクトの名前ではなく、グローバル名「window」です。 person.say() が完了すると、関数呼び出しが完了します。関数呼び出しが終了する前は、 this は person を指しますが、匿名関数が呼び出されるとき、 this は window を指すため、結果は "window" になります。
この問題を解決する方法は、この参照をsay() メソッドの一時変数に割り当てることです。コードは次のとおりです:
var name = "window"; var person = { name : "Leon", age:22, say:function(){ var that = this; return function(){ return that.name; } } } console.info(person.say()()); //控制台输出:Leon
上記は JavaScript クロージャーです。クロージャー内の変数とこのオブジェクトの内容については、PHP 中国語 Web サイト (www.php.cn) に注目してください。