2011-09-09 15 views
0

私は、クロージャとJavaScriptについて読んでてきた、と私はこの試みたまで、私は、それを得たと思った:私はこのコードを走った後 正確にクロージャは何ですか?

var Object5 = function (param) { 
    var x = 0; 

    var squareBrace = function() { 
     return '[' + param + x + ']'; 
    }; 

    this.toString = function() { 
     x = x + 1; 
     return squareBrace(); 
    }; 
}; 

var counter = new Object5("Counter: "); 
print("Has x:" + ('x' in counter)); 
print("Has f:" + ('f' in counter)); 
print("Can access x:" + (!!counter.x)); 
print("Can Invoke f:" + (!!counter.f)); 
print(counter.toString()); 
print(counter.toString()); 
print(counter.toString()); 
print(counter.toString()); 
print(counter.toString()); 
print(counter.toString()); 

そして、それは私が得たものです。

Has x:false 
Has f:false 
Can access x:false 
Can Invoke f:false 
[Counter: 1] 
[Counter: 2] 
[Counter: 3] 
[Counter: 4] 
[Counter: 5] 
[Counter: 6] 

私は「X」と「F」ので、「例外TypeErrorが」「未定義」されるだろうになるだろうと思ったが、その後、私はそれが働いてしまいました。私はクロージャーがこの動作を可能にするものだと考えました。「x」と「y」は「プライベート」であり、クロージャーがなければそれらのメンバーは忘れられます。

明らかに私はそれがすべて間違っている、または私はここで重要な何かを逃しています。

誰かが何のために閉鎖されているのか教えてください。

ありがとうございました。

答えて

3

閉鎖は、スコープ技術です。あるスコープで定義されたパラメータを別のスコープに引き込む方法です。あなたは

var x = 1; 

var f = function(){ 
    console.log(x); // you've just 'closed in' the variable x. 
}; 

f(); 

xを行うと、その関数内で宣言されていないにもかかわらず、機能で利用できるようになります。

具体的な例では、2つの変数が閉鎖されています:paramxです。それらは、定義した関数の範囲にバインドされています。 toStringを実行すると、あなたはxで閉じています。 toStringsquareBraceを実行する場合、その方法ではxparamの両方が使用されます。したがって、変数xの周りに2つのクロージャがあり、それぞれのメソッド(オブジェクト自体のスコープ内にもあります)に1つあります。

+0

私は 'x'の上に2つのクロージャを持っていますが、どちらのメソッドも変数を共有していますか?私は過剰な閉鎖がパフォーマンスに悪いことを読んだ、閉鎖なしで同じことを行う方法はありますか? – vtortola

6

クロージャのトピックに取り組むには、JavaScriptで可変スコープがどのように機能するかを検討する必要があります。基本的な機能と関連した問題を見てみましょう:

この関数は、その後宣言し、変数 counterに値0を割り当て、
function uniqueInteger(){  
    var counter = 0; 
    return ++counter; 
} 

console.log(uniqueInteger()); // returns '1' 
console.log(counter); // undefined 

はインクリメントその値を返します。わかるように、関数が実行されている間は、変数counterは関数内でアクセス可能です。しかし、関数が返った後は、uniqueInteger()関数のスコープの一部であるため、変数は定義されなくなりました。変数counterです。です。機能uniqueInteger()のみがアクセスできます。 しかし、変数の値を次回に呼び出すときには、counterは1で始まり、0ではなく始めるように、変数の値を記憶したければどうでしょうか?1つのアプローチは、関数の外で変数counterを宣言することです。それはもはや私的でなくなり、他の関数はカウンタ変数を変更することができます。

この問題を回避するには、関数の仕組みを調べる必要があります。関数が呼び出されると(上で見たように)、その変数は返された後も存在しなくなります。しかし、その機能の範囲内で、任意のネストされた関数は、まだその親機能の「プライベート」変数にアクセスすることができるようになります:

function uniqueInteger(){  
    var counter = 0; 

    // an example of nested function accessing the counter variable: 
    return (function() { return ++counter })(); 
} 

console.log(uniqueInteger()); // returns '1' 
console.log(counter); // undefined 

*括弧付きの呼び出しの数は()定義された関数の数に一致していることに注意してください。ネストされた関数は、自己起動され、外部関数がラインconsole.log(uniqueInteger());したがって同じ機能で呼び出されるように書くことができます。

function uniqueInteger(){  
    var counter = 0; 

    // an example of nested function accessing the counter variable: 
    return function() { return ++counter }; 
} 

console.log(uniqueInteger()()); // returns '1' 
console.log(counter); // undefined 

私たちが見ることができるように、ネストされた関数は、まだ変数counterにアクセスしていますuniqueInteger()関数は "1"を返します。 uniqueInteger()が返された後もカウンター変数は消えますが(この問題は後で解決します)、ネストされた関数がcounterにアクセスできるという事実により、この変数に対して何かを行い、その結果を返すことができます。 uniqueInteger()が呼び出されるたびに、同じ「スコープチェーン」が存在します。非常に単純化すると、これはレキシカルスコープと呼ばれます。

ここで、関数が復帰した後に消える可変カウンタの問題について説明しましょう。ここで起こるのは、ガベージコレクションと呼ばれるJavaScriptの機能の結果です。関数とその変数がもはや使用されなくなると、関数とその変数は "投げ捨てられ"ます。しかし、その関数の戻り値への参照がある場合(たとえば、その関数が外部変数に割り当てられている場合)、その「スコープチェーン」にしたがって、その内部の変数とともに「記憶」されます上記:

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

var uniqueInt = uniqueInteger(); 

console.log(uniqueInt()); // returns '1' 
console.log(uniqueInt()); // returns '2' 

console.log(counter) // still "undefined" 

何が起こったのですか?ネストされた関数の戻り値を外部変数に保存したため、変数counterはガベージコレクトされず、次回のuniqueInt()が呼び出されたとき、counterはまだ1に等しいと言えます。 。私たちは目標を達成しました。変数を定義しないで呼び出し間の変数を記憶し、非公開にする機能を作成しました。

者らは、上記関数の定義式として、この機能を書き換えることができる:

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

console.log(uniqueInteger()); // returns '1' 
console.log(uniqueInteger()); // returns '2' 

console.log(counter) // still "undefined" 

注二つの関数、したがってつの対応する呼び出しがまだ存在すること。このときだけ外部関数が自己呼び出され、関数自体でなく戻り値を変数uniqueIntegerに保存します。それ以外の場合は、変数uniqueIntegerの戻り値はfunction() { return ++counter; }になります。上で説明した手法は、本質的には、関数内で関数(*またはオブジェクト)を使用して、呼び出しを実行して非公開にしながら内部状態を保存する内部値を操作するクロージャです。

var uniqueInteger = (function(){  
    var counter = 0; 
    return { 
      value: function() { return counter }, 
      count: function() { return ++counter }, 
      reset: function() { counter = 0; return counter;} 
    }; 

})(); 

console.log(uniqueInteger.value()); // 0 
uniqueInteger.count(); 
console.log(uniqueInteger.value()); // 1 
uniqueInteger.reset() 
console.log(uniqueInteger.value()); // again 0 

このパターンは、あなたが持つ独自のプライベート変数で動作する自己完結型の関数オブジェクトを持つことができます:あなたはまた、外部の関数の値を操作する機能を持つオブジェクトを返すことができます*

名前の衝突や悪意のある外部からの改ざんのリスクを排除します。

閉鎖も苦労しましたが、文献を読んでいれば、結局それを得るでしょう。

関連する問題