ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScript クロージャ - クロージャ内の変数とこのオブジェクト

JavaScript クロージャ - クロージャ内の変数とこのオブジェクト

黄舟
黄舟オリジナル
2017-01-20 14:19:351054ブラウズ

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 関数への参照を取得し、ループを通じてページ上の配列の値を出力します。

上記のプログラムのスコープ チェーン メモリ モデルを以下の図に示します。

JavaScript クロージャ - クロージャ内の変数とこのオブジェクト

この図から、 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) に注目してください。


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