js クロージャーについての深い理解 (コードが添付されています)

亚连
リリース: 2018-05-19 14:13:56
オリジナル
1161 人が閲覧しました

この記事では主に js のクロージャについて紹介します。クロージャは js で理解するのが比較的難しい点です。必要な場合は、それを皆さんに共有してください。

クロージャは、特にプログラミングの基礎がない人にとって、js を理解するのが比較的難しい点です。

実際、クロージャに関して注意すべき点はいくつかありますが、それらをすべて理解していれば、克服することは難しくありません。クロージャの基本原則についてお話しましょう。

クロージャの概念

クロージャは、関数と、作成された関数内のスコープ オブジェクトを組み合わせたものです。 (スコープ オブジェクトについては後述します)

もっと簡単に言うと、「1 つ以上の関数が関数内にネストされている限り、それらをクロージャと呼ぶことができます。

これと同様に:

function A() {
 var i = 5;
 return function() {
  console.log('i = '+i);
 }
}

var a = A();
a(); // i = 5
ログイン後にコピー

クロージャの原則

1. 外部関数のローカル変数がクロージャ関数によって呼び出された場合、外部関数の実行直後には再利用されません。

どの言語であっても、オペレーティング システムには、メモリを削減するために余分に割り当てられた領域をリサイクルするガベージ コレクション メカニズムがあることがわかっています。関数のライフサイクルは、関数が呼び出されたときに開始され、関数呼び出しが完了すると、関数内のローカル変数がリサイクル メカニズムによってリサイクルされます。

外部関数 A が呼び出されるとき、A のローカル変数 i はオペレーティング システムによってリサイクルされ、存在しなくなります。ただし、クロージャーを使用すると、結果は次のようになりません。それはリサイクルされません。想像してみてください、i がリサイクルされた場合、返された関数は unknown と出力されませんか?

iなぜリサイクルされないのですか?

JavaScript が関数を実行すると、スコープ オブジェクトが作成され、関数内のローカル変数が保存され (関数の仮パラメーターもローカル変数です)、関数に渡された変数とともに初期化されます。

A が呼び出されるとき、スコープ オブジェクトが作成されます。これを Aa と呼びます。この Aa は次のようになります。 Aa { i: 5 }; A 関数が関数を返した後、 A が実行されます。 Aa オブジェクトはリサイクルされるはずですが、返された関数は Aa の属性 i を使用しているため、返された関数は Aa への参照を保存するため、Aa はリサイクルされません。

したがって、スコープ オブジェクトを理解することで、クロージャが発生したときに関数呼び出しが完了したときに関数のローカル変数がすぐにリサイクルされない理由を理解することができます。

別の例:

function A(age) {
 var name = 'wind';
 var sayHello = function() {
  console.log('hello, '+name+', you are '+age+' years old!');
 };
 return sayHello;
}
var wind = A(20);
wind(); // hello, wind, you are 20 years old!
ログイン後にコピー

そのスコープオブジェクトWwが何であるかわかりますか?

Ww{ age: 20; name: 'wind'; };

2. 外部関数が呼び出されるたびに、新しいクロージャーが生成され、以前のクロージャーは引き続き存在し、相互に影響しません。

3. 同じクロージャは最後の状態を保持し、再度呼び出されるときは前回に基づきます。

外部関数が呼び出されるたびに生成されるスコープ オブジェクトは異なると考えることができます。上記の例では、渡すパラメータ age が毎回異なるため、生成されるオブジェクトも毎回異なります。

外部関数が呼び出されるたびに、新しいスコープ オブジェクトが生成されます。

function A() {
 var num = 42;
 return function() { console.log(num++); }
}
var a = A();
a(); // 42
a(); // 43

var b = A(); // 重新调用A(),形成新闭包
b(); // 42
ログイン後にコピー

このコードにより、2 つのことがわかります。まず、a(); を 2 回連続して呼び出すと、num が元の基準でインクリメントされます。これは、同じクロージャが最後の状態を保持し、再度呼び出されるときは前回に基づくことを意味します。 2. b() の結果は 42 で、これは新しいクロージャであり、他のクロージャの影響を受けないことを示しています。

シャボン玉を吹くのと同じように、シャボン玉を吹く(外部関数を呼び出す)たびに、新しいシャボン玉(閉鎖)が同時に生成されると考えることができます。 2 つのシャボン玉は同時に存在できますが、互いに影響を与えることはありません。

4. 外部関数に存在する複数の関数は「生きて死ぬ」

以下の3つの関数は同時に宣言され、スコープオブジェクトのプロパティ(ローカル変数)にアクセスして操作することができます。

var fun1, fun2, fun3;
function A() {
 var num = 42;
 fun1 = function() { console.log(num); }
 fun2 = function() { num++; }
 fun3 = function() { num--; } 
}

A();
fun1();  // 42
fun2(); 
fun2(); 
fun1();  // 44
fun3(); 
fun1();  //43

var old = fun1;

A(); 
fun1();  // 42
old();  // 43  上一个闭包的fun1()
ログイン後にコピー

関数は複数の戻り値を持つことができないので、グローバル変数を使用しました。もう一度、A() を 2 回呼び出すときに新しいクロージャが作成されることがわかります。

クロージャがループ変数に遭遇したとき

クロージャについて話すときは、次のコードを見てください:

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    var item = &#39;item&#39; + i;
    result.push( function() {console.log(item + &#39; &#39; + arr[i])} );
  }
  return result;
}

var fnlist = buildArr([1,2,3]);
fnlist[0](); // item2 undefined
fnlist[1](); // item2 undefined
fnlist[2](); // item2 undefined
ログイン後にコピー

どうやってこれが起こるでしょうか。起こる? ?私たちが想定する 3 つの出力は、item0 1、item1 2、item2 3 となるはずです。返された結果の配列に未定義の item2 が 3 つ格納されているのはなぜですか?

原来当闭包遇到循环变量时都是循环结束之后统一保存变量值,拿我们上面的例子来说,i是循环变量,当循环全部结束的时候i正好是i++之后的3,而arr[3]是没有值的,所以为undefined,有人会疑惑:为什么item的值是item2,难道不应该是item3吗?注意,在最后一次循环的时候也就是i = 2的时候,item的值为item2,当i++,i = 3循环条件不满足循环结束,此时的item的值已经定下来了,所以此时的arr[i]为arr[3],而item为item2。这样能理解吗?如果我们将代码改成这样那就说得通了:

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) { 
    result.push( function() {console.log(&#39;item&#39; + i + &#39; &#39; + arr[i])} );
  }
  return result;
}

var fnlist = buildArr([1,2,3]);
fnlist[1](); // item3 undefined
ログイン後にコピー

那么问题来了,如何改正呢?且看代码:

function buildArr(arr) {
  var result = [];
  for (var i = 0; i < arr.length; i++) {
    result.push( (function(n) {
      return function() {
       var item = &#39;item&#39; + n;
       console.log(item + &#39; &#39; + arr[n]);
      }
    })(i));
  }
  return result;
}

var fnlist = buildArr([1,2,3]);
fnlist[0](); // item0 1
fnlist[1](); // item1 2
fnlist[2](); // item2 3
ログイン後にコピー

我们可以用一个自执行函数将i绑定,这样i的每一个状态都会被存储,答案就和我们预期的一样了。

所以以后在使用闭包的时候遇到循环变量我们要习惯性的想到用自执行函数来绑定它。

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

js 闭包常见的两种情况的解析

浅谈js 闭包引起的内存泄露问题_javascript技巧

让你分分钟学会 JS 闭包

以上がjs クロージャーについての深い理解 (コードが添付されています)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート