nodejsは閉じます
14151 ワード
一、クローズドとは何ですか?
公式」の説明は、クローズドは多くの変数とこれらの変数を結びつける環境を持つ表現であり、これらの変数もこの式の一部であるということです.この言葉を直接読む人は少ないと思います.彼の説明はあまりにも学術的なものですから.実はこの言葉は分かりやすく言えば、JavaScriptの中のすべてのfunctionは一つのクローズドです.しかし、一般的には、ネストされたfunctionによって生じたクローズドがより強く、ほとんどの場合、私たちがいわゆる「クローズド」と呼んでいます.下のコードを見てください.
1、関数bが関数aの内部にネストされている.
2、関数aは関数bを返します.
引用関係は図の通りです.
このように、var c=a()を実行した後で、変数cは実際に関数bを指して、c()を実行した後に1つのウィンドウを弾いてiの値を表示します(初めては1です).このコードは実は一つのクローズドを作成しました.なぜですか?関数a以外の変数cは関数a内の関数bを参照しているからです.
関数aの内部関数bが関数a以外の変数によって参照されると、クローズドパケットが作成されます.
もっと徹底的に話しましょう.「クローズド」とは、ターゲットオブジェクトの方法関数として他の関数をコンストラクタ内で定義することであり、このオブジェクトの方法関数は、外層関数内の臨時変数を逆に参照することである.これにより、オブジェクトが生存期間内にその方法を維持することができれば、元の構造関数がその時に使用した臨時変数値を間接的に維持することができる.最初のコンストラクタコールは終了し、一時変数の名前も消えましたが、対象オブジェクトの方法では常に変数の値を参照することができます.この値はこの方法でしかアクセスできません.同じコンストラクタを再度呼び出しても、新しいオブジェクトと方法だけが生成されます.新しい一時変数は新しい値に対応するだけで、前回の呼び出しはそれぞれ独立しています.
二、カバンを閉じて何の効果がありますか?
簡単に言えば、クローズドの役割は、aが実行されてから戻ってきた後、クローズドはJavascriptのゴミ回収メカニズムGCがaによって占有された資源を回収しないようにします.aの内部関数bの実行はaの変数に依存します.これは閉包作用に対する非常にはっきりした説明で、専門でもないし、厳格でもないが、大体の意味はこのようにして、閉包を理解するには順序を追って漸進する過程が必要である.
上記の例では、関数aが戻ると、aのiは常に存在するので、c()を実行する毎に、iはプラス1の後、alertからiの値を出す.
aが関数bでない場合は、別の状況を想像します.aが実行された後、bはaの外界に戻されず、aに引用されただけで、この時aもbに引用されるだけで、関数aとbは相互に参照しても外界に邪魔されず、関数aとbはGCに回収される.(Javascriptのゴミ回収メカニズムについては後述する)
三、クローズドのミクロの世界
クローズド及び関数aとネスト関数bとの関係をより深く理解するためには、関数の実行環境、アクティブオブジェクト、スコープ、スコープチェーンの他のいくつかの概念を導入する必要があります.これらの概念は、関数aが定義から実行までのプロセスを例に挙げて説明される.
関数aを定義すると、js解釈器は、関数aのスコープチェーンをaを定義するときaがある環境を設定します.aが大域関数である場合、scope chainにはwindowオブジェクトしかありません.関数aを実行すると、aは対応する実行環境に入ります.実行環境を作成する過程で、まずaにscope属性、すなわちaの作用領域を追加します.その値は第1ステップのscope chainです.a.scope=aの作用ドメインチェーンです.そして実行環境は活動対象を作成します.オブジェクトも属性を持つオブジェクトですが、プロトタイプがなく、JavaScriptコードで直接アクセスできません.アクティブなオブジェクトを作成したら、アクティブなオブジェクトをaのアクティブなドメインチェーンの一番上に追加します.この時、aの作用領域チェーンは2つのオブジェクトを含んでいます.aの活動対象とwindowオブジェクトです.次のステップは、関数aを呼び出したときに渡されるパラメータを保存したargments属性をアクティブオブジェクトに追加します.最後に、すべての関数aのイメージと内部の関数bの参照をaのアクティブオブジェクトにも追加します.このステップでは、関数bの定義が完了し、したがって、第3ステップのように、関数bの作用ドメインチェーンがbで定義される環境、すなわちaの作用領域に設定される.これで関数a全体が定義から実行までのステップが完了します.このときaは関数bの参照をcに返し、また関数bの作用ドメインチェーンは関数aの活動対象への参照を含み、つまりbはaで定義されたすべての変数と関数にアクセスすることができる.関数bはcによって参照され、関数bはまた関数aに依存しているので、関数aは帰った後にGCによって回収されない.
関数bが実行されると上記のようになります.このため、実行時bのスコープには3つのオブジェクトが含まれています.bの活動対象、aの活動対象、windowオブジェクトは、下図のようになります.
図に示すように、関数bで変数にアクセスすると、検索順序は次のようになります.
まず自身の活動対象を検索し、存在する場合は戻ります.関数aの活動対象が存在しない場合は、検索を継続して、検索します.プロトタイプのオブジェクトが関数bに存在する場合は、自身のオブジェクトを検索した後、プロトタイプのオブジェクトを探してから検索します.これはJavascriptの変数検索の仕組みです.スコープ全体が見つからない場合、undefinedに戻ります.結び目は、このセクションで2つの重要な単語を述べました.関数の定義と実行.本文で関数のスコープは、実行時に決定されるのではなく、関数を定義するときに決定されます.この問題をコードで説明します.
関数hの作用領域がalert(h()を実行して決定されると仮定すると、この時hの作用領域チェーンは、hのアクティブオブジェクト->alertのアクティブオブジェクト->windowオブジェクトである.関数hの作用領域は定義時に決定されると仮定すると、hが指す匿名関数は定義時に既に作用領域を決定している.では、実行時、hの作用ドメインチェーンは、hの活動対象->fの活動対象->windowオブジェクトです.もし最初の仮定が成立したら、出力値はundefinedです.第二の仮説が成立すれば、出力値は1となる.
実行結果は第2の仮説が正しいことを証明しており、関数の作用域は確かにこの関数を定義するときに決定された.
四、クローズドの応用シーン
関数内の変数の安全を保護します.最初の例では、関数aのiは関数bのみでアクセスでき、他の経路でアクセスできないので、iのセキュリティが保護されている.
メモリ内で変数を維持します.前の例のように、関数a中iはメモリに存在しているので、c()を実行するたびに、iに1を加算します.変数の安全を保護することによって、JSのプライベート属性とプライベートメソッド(外部にアクセスできない)のプライベート属性と方法は、Costructorの外ではアクセスできません.
五、Javascriptのゴミ回収メカニズム
Javascriptでは、オブジェクトがもう引用されないと、このオブジェクトはGCによって回収されます.2つのオブジェクトが相互に参照され、第三者から引用されなくなると、この2つの相互参照の対象も回収されます.関数aはbによって引用され、bはa以外のcによって引用されるので、これは関数aが実行されても回収されない理由です.
thisの針について
公式」の説明は、クローズドは多くの変数とこれらの変数を結びつける環境を持つ表現であり、これらの変数もこの式の一部であるということです.この言葉を直接読む人は少ないと思います.彼の説明はあまりにも学術的なものですから.実はこの言葉は分かりやすく言えば、JavaScriptの中のすべてのfunctionは一つのクローズドです.しかし、一般的には、ネストされたfunctionによって生じたクローズドがより強く、ほとんどの場合、私たちがいわゆる「クローズド」と呼んでいます.下のコードを見てください.
function a() {
var i = 0;
function b() { alert(++i); }
return b;
}
var c = a();
c();
このコードには二つの特徴があります.1、関数bが関数aの内部にネストされている.
2、関数aは関数bを返します.
引用関係は図の通りです.
このように、var c=a()を実行した後で、変数cは実際に関数bを指して、c()を実行した後に1つのウィンドウを弾いてiの値を表示します(初めては1です).このコードは実は一つのクローズドを作成しました.なぜですか?関数a以外の変数cは関数a内の関数bを参照しているからです.
関数aの内部関数bが関数a以外の変数によって参照されると、クローズドパケットが作成されます.
もっと徹底的に話しましょう.「クローズド」とは、ターゲットオブジェクトの方法関数として他の関数をコンストラクタ内で定義することであり、このオブジェクトの方法関数は、外層関数内の臨時変数を逆に参照することである.これにより、オブジェクトが生存期間内にその方法を維持することができれば、元の構造関数がその時に使用した臨時変数値を間接的に維持することができる.最初のコンストラクタコールは終了し、一時変数の名前も消えましたが、対象オブジェクトの方法では常に変数の値を参照することができます.この値はこの方法でしかアクセスできません.同じコンストラクタを再度呼び出しても、新しいオブジェクトと方法だけが生成されます.新しい一時変数は新しい値に対応するだけで、前回の呼び出しはそれぞれ独立しています.
二、カバンを閉じて何の効果がありますか?
簡単に言えば、クローズドの役割は、aが実行されてから戻ってきた後、クローズドはJavascriptのゴミ回収メカニズムGCがaによって占有された資源を回収しないようにします.aの内部関数bの実行はaの変数に依存します.これは閉包作用に対する非常にはっきりした説明で、専門でもないし、厳格でもないが、大体の意味はこのようにして、閉包を理解するには順序を追って漸進する過程が必要である.
上記の例では、関数aが戻ると、aのiは常に存在するので、c()を実行する毎に、iはプラス1の後、alertからiの値を出す.
aが関数bでない場合は、別の状況を想像します.aが実行された後、bはaの外界に戻されず、aに引用されただけで、この時aもbに引用されるだけで、関数aとbは相互に参照しても外界に邪魔されず、関数aとbはGCに回収される.(Javascriptのゴミ回収メカニズムについては後述する)
三、クローズドのミクロの世界
クローズド及び関数aとネスト関数bとの関係をより深く理解するためには、関数の実行環境、アクティブオブジェクト、スコープ、スコープチェーンの他のいくつかの概念を導入する必要があります.これらの概念は、関数aが定義から実行までのプロセスを例に挙げて説明される.
関数aを定義すると、js解釈器は、関数aのスコープチェーンをaを定義するときaがある環境を設定します.aが大域関数である場合、scope chainにはwindowオブジェクトしかありません.関数aを実行すると、aは対応する実行環境に入ります.実行環境を作成する過程で、まずaにscope属性、すなわちaの作用領域を追加します.その値は第1ステップのscope chainです.a.scope=aの作用ドメインチェーンです.そして実行環境は活動対象を作成します.オブジェクトも属性を持つオブジェクトですが、プロトタイプがなく、JavaScriptコードで直接アクセスできません.アクティブなオブジェクトを作成したら、アクティブなオブジェクトをaのアクティブなドメインチェーンの一番上に追加します.この時、aの作用領域チェーンは2つのオブジェクトを含んでいます.aの活動対象とwindowオブジェクトです.次のステップは、関数aを呼び出したときに渡されるパラメータを保存したargments属性をアクティブオブジェクトに追加します.最後に、すべての関数aのイメージと内部の関数bの参照をaのアクティブオブジェクトにも追加します.このステップでは、関数bの定義が完了し、したがって、第3ステップのように、関数bの作用ドメインチェーンがbで定義される環境、すなわちaの作用領域に設定される.これで関数a全体が定義から実行までのステップが完了します.このときaは関数bの参照をcに返し、また関数bの作用ドメインチェーンは関数aの活動対象への参照を含み、つまりbはaで定義されたすべての変数と関数にアクセスすることができる.関数bはcによって参照され、関数bはまた関数aに依存しているので、関数aは帰った後にGCによって回収されない.
関数bが実行されると上記のようになります.このため、実行時bのスコープには3つのオブジェクトが含まれています.bの活動対象、aの活動対象、windowオブジェクトは、下図のようになります.
図に示すように、関数bで変数にアクセスすると、検索順序は次のようになります.
まず自身の活動対象を検索し、存在する場合は戻ります.関数aの活動対象が存在しない場合は、検索を継続して、検索します.プロトタイプのオブジェクトが関数bに存在する場合は、自身のオブジェクトを検索した後、プロトタイプのオブジェクトを探してから検索します.これはJavascriptの変数検索の仕組みです.スコープ全体が見つからない場合、undefinedに戻ります.結び目は、このセクションで2つの重要な単語を述べました.関数の定義と実行.本文で関数のスコープは、実行時に決定されるのではなく、関数を定義するときに決定されます.この問題をコードで説明します.
function f(x) {
var g = function () { return x; }
return g;
}
var h = f(1);
alert(h());
このコード内の変数hは、f中のその匿名関数(gで返される)を指している.関数hの作用領域がalert(h()を実行して決定されると仮定すると、この時hの作用領域チェーンは、hのアクティブオブジェクト->alertのアクティブオブジェクト->windowオブジェクトである.関数hの作用領域は定義時に決定されると仮定すると、hが指す匿名関数は定義時に既に作用領域を決定している.では、実行時、hの作用ドメインチェーンは、hの活動対象->fの活動対象->windowオブジェクトです.もし最初の仮定が成立したら、出力値はundefinedです.第二の仮説が成立すれば、出力値は1となる.
実行結果は第2の仮説が正しいことを証明しており、関数の作用域は確かにこの関数を定義するときに決定された.
四、クローズドの応用シーン
関数内の変数の安全を保護します.最初の例では、関数aのiは関数bのみでアクセスでき、他の経路でアクセスできないので、iのセキュリティが保護されている.
メモリ内で変数を維持します.前の例のように、関数a中iはメモリに存在しているので、c()を実行するたびに、iに1を加算します.変数の安全を保護することによって、JSのプライベート属性とプライベートメソッド(外部にアクセスできない)のプライベート属性と方法は、Costructorの外ではアクセスできません.
function Constructor(...){
var that = this;
var membername = value;
function membername(...){...}
}
以上の3つのポイントは、クローズドの最も基本的なアプリケーションシーンであり、多くの古典的なケースがここから由来しています.五、Javascriptのゴミ回収メカニズム
Javascriptでは、オブジェクトがもう引用されないと、このオブジェクトはGCによって回収されます.2つのオブジェクトが相互に参照され、第三者から引用されなくなると、この2つの相互参照の対象も回収されます.関数aはbによって引用され、bはa以外のcによって引用されるので、これは関数aが実行されても回収されない理由です.
thisの針について
// , this module.exports。
console.log(this); //{}
this.num = 10;
console.log(this.num); //10
console.log(global.num); //undefined
console.log(module.exports.num); //10
//第二の場合、関数の中でthisが指すのはglobalオブジェクトで、全体の中のthisとは同じオブジェクトではなく、簡単に言えば、関数の中でthisによって定義された変数はglobalに属性を追加したものに相当します.この場合、グローバルのthisとはもう関係がありません.// :
function fn(){
this.num = 20;
}
fn();
console.log(this); //{}
console.log(this.num); //undefined
console.log(global.num); //20
//
function fn(){
function fn2(){
this.age = 18;
}
fn2();
console.log(this); //global
console.log(this.age); //18
console.log(global.age); //18
}
fn();
//第三の場合、構造関数でthisが指し示すのはその例で、globalではない.function Fn(){
this.num = 998;
}
var fn = new Fn();
console.log(this); //{}
console.log(fn.num); //998
console.log(global.num); //undefined