JavaScriptでの呼び出し対象となるスコープは閉鎖されます.

5677 ワード

JavaScriptでは、すべての関数以外で宣言される変数は大域変数であり、関数内で宣言される変数(varキーワードを通じて)は局所変数である.実際、グローバル変数はグローバルオブジェクトの属性です.例えば、クライアントのJavaScriptにおいて、私たちが宣言した変数はwindowオブジェクトの属性です.
      局所変数はどのような対象に属しますか?私たちが検討する呼び出し対象です.関数を実行すると、関数のパラメータとその局所変数が呼び出し対象の属性として記憶されます.同時に、インタプリタは関数のためにアクチュエータのコンテキストを作成します.コンテキストに対応するのは作用ドメインチェーンです.名前の通り、作用域チェーンは作用域に関するチェーンであり、通常はチェーンテーブルとして実現され、チェーンテーブルの各項目はいずれも一つのオブジェクトであり、大域作用域において、このチェーンには一つのオブジェクト、すなわちグローバルオブジェクトだけがあります.対応する関数では、スコープ上に2つのオブジェクトがあります.最初の(最初にアクセスされた)は呼び出しオブジェクトで、2番目はグローバルオブジェクトです.
関数が変数を使用する必要がある場合、インタプリタはドメインチェーンを巡回します.
var str = "global";
function scopeTest(){
    print(str);
    var str = "local";
    print(str);
}
解釈器がscopeTest関数に入ると、一つの呼び出し対象が作成され、その中の一つの属性としてstr変数が含まれていて、undefinedに初期化され、最初のprint(str)が実行されると、解釈器は、スコープチェーン内でstrを検索し、見つけたらundefinedとして印刷し、値付け文を実行します.このとき、オブジェクトの属性strはlocalに割り当てられますので、2番目のprint文はlocalを印刷します.
      注目すべきは、スコープ関数の階層に従って作用するスコープが長くなりますが、変数を検索するプロセスは依然としてロールフィールドチェーン(チェーンテーブル)を経て、その値が見つかるまで上に検索されています.効果ドメインチェーンがまだ対応する属性が見つからない場合、変数は定義されていません.(未割当値を定義してundefinedに戻り、未定義で運転を引き起こすエラーnot defined)
      宣言されていない変数の値を読んでみます.Javascriptはエラーを発生します.var宣言を使用している変数の割り当てを試みると、Javascriptはこの変数を暗黙的に宣言します.暗黙的宣言の変数は常に大域変数として作成されます.たとえ変数は一つの関数の中でのみ使用されます.ローカル変数を作成する際にグローバル変数を作成することを防ぐためには、関数体内部でvar文を使用する必要があります.
    一つの関数の呼び出しオブジェクトはダイナミックで、この関数が呼び出された時にのみ実装されます.関数が定義されているときにその作用分域鎖が決定されることを知っています.Javascriptインタプリタが関数を呼び出すと、このスコープの前に新しいオブジェクトが追加されます.この呼び出し対象の属性の一つは、アーグメンツオブジェクトが関数の実際のパラメータであるというアラグメンントという属性に初期化される.すべてのvar文で宣言されているローカル変数も、この呼び出し対象に定義されています.このとき、呼び出し対象はスコープのヘッダにあり、ローカル変数、関数形式パラメータ、アーグメンツオブジェクトはすべてこの関数の範囲にあります.もちろん、このときはローカル変数と関数形式パラメータとAgmentsオブジェクトが、作用ドメインチェーン内の同名の属性をカバーします.
スコープ、スコープ、呼び出しオブジェクト間の関係
関数が定義されているとき、実際にはその外層関数が実行されるとき、その決定された作用域チェーンは実際にその外層関数の呼び出し対象チェーンである.関数が呼び出されると、そのスコープは定義に従って決定されたスコープチェーン(外部関数の呼び出し対象チェーン)に実用化された呼び出しオブジェクトを付加する.したがって、関数のスコープは実際にオブジェクトチェーンを呼び出します.関数が呼び出されたとき、そのスコープチェーン(または呼び出し対象チェーン)は実際に定義されたときに決定されるスコープチェーンの一つのスーパーセットである.それらの間の関係は、作用領域⊃__がドメインチェーン⊇を使ってオブジェクトを呼び出すと表してもよい.
function f(x) {
    var g = function () { return x; }
    return g;
}
var g1 = f(1);
alert(g1());  //   1
は、大局を以下のような大きな匿名関数と見なすと仮定する.
(function() { //        })();
の例は、次のように見える.
(function() {
    function f(x) {
        var g = function () { return x; }
        return g;
    }
    var g1 = f(1);
    alert(g1());  //   1
})();
  • 大域の大匿名関数が定義されると、外層がないので、その作用領域チェーンは空です.
  • グローバルの大匿名関数が直接実行され、グローバルのスコープには「グローバル呼び出しオブジェクト」が一つしかない.
  • 関数fが定義されており、このとき関数fの作用領域チェーンは、その外層の作用領域チェーン、すなわち「グローバル呼び出しオブジェクト」である.
  • 関数f(1)は、新しいf(1)呼び出しオブジェクトに関数fが定義されたときの作用領域チェーン、すなわち'f(1)呼び出しオブジェクト->グローバル呼び出しオブジェクト'である.
  • 関数g(g 1に戻されるなら、g 1と名付けましょう)はf(1)で定義されていますが、その作用域チェーンはその外層の関数f(1)の作用域チェーン、すなわち「f(1)呼び出しオブジェクト-」グローバルコールオブジェクトです.
  • 関数f(1)は、関数gの定義をg 1に返す.
  • 関数g 1は、新しいg(1)呼び出しオブジェクトに外層f(1)を付加した作用ドメインチェーン、すなわち'g 1呼び出しオブジェクト->f(1)呼び出しオブジェクト->グローバル呼び出しオブジェクト'である.
  • クローズドClouer
    クローズドの一つの簡単な言い方は、ネスト関数がネスト関数の外注によって使用されると、クローズドが形成されるということです.
    前の例は実は一つのクローズドです.g 1はf(1)内部で定義されていますが、f(1)が戻ってから実行されます.閉ループの効果の一つは、ネスト関数fによって返された後、その内部のリソースが解放されないことを示しています.g関数を外部から呼び出した場合、gはfの内部変数にアクセスできます.この特性によって、多くの優雅なコードが書けます.
    例えば、一つのページに統一されたカウンタを作成します.
    var counter  = (function() {
        var i = 0;
        var fns = {"get": function() {return i;},
                   "inc": function() {return ++i;}};
        return fns;
    })();
    //do something
    counter.inc();
    //do something else
    counter.inc();
    var c_value = counter.get();  //now c_value is 2
    このようにメモリには変数iが維持されています.プログラム全体の他の場所では、iの値を直接操作することはできません.
    setTimeout(fn,delay)の場合は、fnという関数のハンドルにパラメータを伝えることはできませんが、必要なパラメータをクローズドする方法でfn内部にバインドすることができます.
    for(var i=0,delay=1000; i< 5; i++, delay +=1000) {
        setTimeout(function() {
            console.log('i:' + i + " delay:" + delay);
        }, delay);
    }
    このようにプリントした値は全部
    i:5 delay:6000 i:5 delay:6000 i:5 delay:6000 i:5 delay:6000 i:5 delay:6000
    クローズドにすると、伝えたいパラメータをバインドしやすくなります.
    for(var i=0, delay=1000; i < 5; i++, delay += 1000) {
        (function(a, _delay) { 
            setTimeout(function() { 
                console.log('i:'+a+" delay:"+_delay);
            }, _delay);
        })(i, delay);
    }
    出力:
    i:0 delay:1000 i:1 delay:2000 i:2 delay:3000 i:3 delay:4000 i:4 delay:5000
    クローズドはもう一つよく使われているところがあります.イベントを結びつけるコールバック関数の時です.同様の道理で、結合された関数のハンドルはパラメータを作ることができませんが、閉じた形でパラメータを結合することができます.
    締め括りをつける
  • 関数の品詞作用ドメインと作用ドメインチェーンは異なるものであり、語法作用ドメインは抽象概念であり、作用ドメインチェーンは実用化されたコール対象チェーンである.
  • 関数は、定義されたときに、その外層の関数として実行される場合でもある.
  • 関数は定義された時にその語法のスコープは確定されましたが、依然として抽象的な概念で、なくても実用化されません.
  • 関数は、定義された時に、その外層関数の作用領域チェーンであるものを決定しました.これは実用化されたものです.
  • 関数は、複数回にわたって呼び出されると、その作用ドメインチェーンは異なる.
  • クローズドが強いです.サイの本は正しいです.これらを理解すれば、高級Javascriptプログラマーと自称することができます.これらの概念をうまく利用することで、Javascriptの多くのデザインパターンを楽しむことができます.