2016-04-25 28 views
1

に自己呼び出し関数内の変数の寿命は何ですか:私は、次のコードを理解しようとしてきたのjavascript

var add = (function() { 
     var counter = 0; 
     return function() {return counter += 1;} 
    })(); 
    add(); 
    add(); 
    add(); 

ここaddは匿名の自己呼び出し関数の戻り値を割り当てられている - ということは、機能はfunction() { return counter += 1 }です。今すぐ初めてadd()が呼び出されると、期待通りに1が返されます。しかし、2回目にadd()と呼ばれると、2が返されます。

私の質問は、counterが関数内で定義されているので、関数が実行を終了するたびにcounterが死んでしまうのではないでしょうか?つまり、add()への最初の呼び出し後に1が表示されます。今我々はその機能から外れているので、counterはそれが以前の値を忘れて、automaticのようなスタックから破壊されるべきではない?

+1

http://stackoverflow.com/questions/111102/how-do-javascript-closures-work – mplungjan

+0

クロージャの世界へようこそ。私は、この概念を匿名関数の概念とはっきりと区別するために、自己呼び出し関数(IIFE)ではなく通常の関数を使って遊ぶことをお勧めします(90年代後半から2000年初頭の早い段階でコミュニティの多くの人々が一部の他の言語では匿名関数がクロージャと呼ばれることには役立ちません) – slebetman

+0

[auto]タグはC++ 11固有のものであり、除去された。将来あなたの質問にタグを付ける方法に注意してください。 –

答えて

7

JavaScriptで

自己呼び出し関数内の変数の寿命は、JavaScriptの機能の他の種類の変数と同じでは何です:彼らは、時々、限り、彼らが参照できるようするために存在しますそれらを含む関数が返ってくるまでの時間を意味します。関数は、それが作成して返す(return function() {return counter += 1;})は可変オーバー閉鎖あるため

あなたcounter変数は、生命維持に戻った後に存在し続けています。変数は、その関数が存在する限り存在します。

はもっと技術的に:関数を呼び出すと変数的環境オブジェクトを持って、その呼び出しのための実行コンテキストと呼ばれるものを作成します。呼び出し中に作成された関数は、その外部の変数環境オブジェクトへの参照を受け取ります。そのオブジェクトは、すべてのオブジェクトと同様に、そのオブジェクトへの参照が存在する限り存在し、関数はオブジェクトを生かしています。変数は実際にはその変数環境オブジェクトのプロパティなので、何かがそれらのオブジェクトを参照する限り存在します。 (これは非常に単純化されたフォームです)理論的には変数環境オブジェクト全体が保持されていますが、実際にはJavaScriptエンジンは最適化の効果が観測できない場合は自由に最適化できるため、クロージャで実際には使用されない変数(またはそうでないかもしれない)は、エンジンと機能のコードに応じてリリースされるかもしれません。

あなたのIIFEは一度だけ呼び出すことができるので、counterが1つしかない可能性がありますが、複数の変数オブジェクトがある場合はクロージャを複数回呼び出す関数によくあります。閉鎖された変数。

例:上記で

function helloBuilder(name) { 
 
    var counter = 0; 
 
    return function() { 
 
    ++counter; 
 
    display("Hi there, " + name + ", this is greeting #" + counter); 
 
    }; 
 
} 
 

 
var helloFred = helloBuilder("Fred"); 
 
var helloMaria = helloBuilder("Maria"); 
 

 

 
helloFred(); // "Hi there, Fred, this is greeting #1" 
 
helloFred(); // "Hi there, Fred, this is greeting #2" 
 
helloMaria(); // "Hi there, Maria, this is greeting #1" 
 
helloMaria(); // "Hi there, Maria, this is greeting #2" 
 
helloFred(); // "Hi there, Fred, this is greeting #3" 
 

 
function display(msg) { 
 
    var p = document.createElement('p'); 
 
    p.appendChild(document.createTextNode(msg)); 
 
    document.body.appendChild(p); 
 
}

helloBuilderによって返される関数はにわたってそのname引数の両方とそのcounter変数を閉じます。 (引数も実行コンテキストの変数オブジェクトに格納されるためです。)したがって、2回呼び出すと、それぞれがnamecounterの2つの変数オブジェクトがあり、1つはそれぞれの関数によって参照され、helloBuilderに作成することがわかります。

+0

私はあなたに正しいと言えば、関数をグローバル変数 'add'に代入しているので意味します。関数内のすべてがカウンタ変数を含むスタックに保存されます。カウンタは一種の変数として機能します。 – user31782

+1

@ user31782:まあまあです。スタック自体は、配列ではなくリンクリストとして設計されているため、クロージャが作成されたときにスタックフレームを切り離して再接続することができます。クロージャがスタックにどのように関連しているかについての概念的な低レベルの詳細については、この回答を参照してください。http://stackoverflow.com/questions/26061856/javascript-cant-access-private-properties/26063201#26063201 – slebetman

+1

@ user31782: add 'はグローバルである必要はなく、変数オブジェクトの内容はスタックに格納されない。それ以外は、はい。 IIFEが返す関数への参照を保持するので、IIFE呼び出しの変数オブジェクトは、その関数を保持している限り保持されます。そして、Cの静的変数のように*ビット*ですが、関数*への呼び出しごとに*が1つあります。あなたのIIFEは一度だけ呼び出すことができますが、これはあなたの例に特有のものです。複数回呼び出すことができる関数を返す関数が一般的です。 –

関連する問題