先端攻撃の巨人(三):スコープからクローズドに入る

7581 ワード

進撃する巨人の第3編、本編は作用域、作用域チェーン、閉鎖などの知識点について、一つ一つ撃破します.
スコープ
スコープ:すべての宣言の識別子(変数)からなる一連のクエリを収集し、維持し、非常に厳格な規則を実施し、現在実行されているコードがこれらの識別子(変数)に対するアクセス権限を決定する.
——「知らないJavaScript上巻」
作用域は縄張りのようです.みんなで地域を作って、それぞれ管理しています.井戸水は川の水を犯さないです.
var globaValue = '       ';
function foo() {
    var fooValue = '  foo   ';
    function bar() {
        var barValue = '  bar   ';
    }
}

function other() {
    var otherValue = '  other   ';
}
スコープの変数宣言
異なる作用領域で同じ変数に名前を付けると衝突しません.「近原則」を選択します.
var name = '    ';
function getName() {
    var name = '    ';
    console.log(name);    // '    '
}
console.log(name);        // '    '
スコープの種類
コンテキスト環境を実行するには、グローバル、関数、evalがあります.作用領域も三つあり、ES 6はブロックレベルの作用領域を追加した.
  • グローバルスコープ
  • 関数領域
  • evalスコープ(evalの使用を推奨しないので、一時的に無視する)
  • ブロックレベルのスコープ(ES 6追加)
  • グローバルスコープ
    JavaScriptではグローバル環境は一つしかなく、対応するグローバルスコープも一つしかない.局所環境がvar/let/constを使用していないと宣言した変数のデフォルトは、グローバル変数になります.
    function foo() {
        a = 10;
    };
    foo();
    console.log(a);    // 10      (      )
    関数のスコープ
    ES 6の前に、局所的な作用領域を実現する方法は、いずれも関数に変数を宣言することによって実現されるので、関数作用領域とも呼ばれる.ネストの複数をサポートする.
    var a = 20;
    function foo() {
        var a = 10;
        console.log(a);    // 10;
    }
    foo();
    関数で変数を宣言する場合は、関数の先頭部分にすべての変数を宣言し、参照しやすくし、手振れを防ぐためにvar/let/constで宣言します.
    function getClient() {
        var name;
        var phone;
        var sex;
    }
    ブロックレベルのスコープ
    まず、ブロックとは何かを理解しましょう.ブロックとは、大かっこ{}に包まれたコード部分のことです.
    if (true) {
        //       ,      
    }
    ES 6の前にはブロックレベルのスコープという概念がなかったので、{}には自分のスコープがありませんでした.ES 5の環境下でブロックレベルのスコープを構築したいなら、一般的には即座に関数を実行することによって実現される.
    var name = '    ';
    (function(window) {
        var name = '    ';
        console.log(name);    // '    '
    }(window));
    console.log(name);        // '    '
    ES 5は、関数のスコープを使ってブロックレベルのスコープを実現する方式で、コードを読むのに不便なため、多くの即時実行関数(IIIFSE)をコードで満たすことができます.良いコードのは良い文章と同じで、読む人にすっきりと分かります.
    このため、ES 6はブロックレベルのスコープの概念を追加し、let/constを使用して変数を宣言する方式で、そのスコープをコードブロックに指定することができ、関数スコープと同様にネストをサポートする.
    let i = 0;
    for (let i = 0; i < 10; i++){
        console.log(i);
    }
    i;    // 0
    let/constは変数の昇格を許さないので、「先に声明してから使用する」必要があります.このような制限は、「一時性死区」と呼ばれています.これはコードの作成段階でより規範化し、実行と書き込みの順序を一致させることができます.
    スコープチェーン(変数クエリ規則)
    変数がスコープによって管理されると、変数のスコープ内の検索規則は、いわゆるスコープチェーンです.
    作用するドメインチェーンの用途は、実行環境にアクセスできるすべての変数と関数に対する規則的なアクセスを保証することである.
    ——「JavaScript高級プログラム設計」
    現在実行されている環境で使用されている変数を検索し始めます.見つかったらその値を返します.見つけられない場合は、グローバルスコープまで階層ごとに上位(親のスコープ)を検索します.
    var money = 100;
    function foo() {
        function bar() {
            console.log(money);
        }
        bar();
    }
    foo();
    自由変数
    変数はたくさん見ましたが、「自由変数」は結構仰々しいです.実はそれに対して、私達はよく知らないのです.
    「自由変数:現在実行されている環境で使用されていますが、現在環境宣言を実行していない変数(関数パラメータargments除外)」
    関数呼び出し時には、コンテキスト作成段階に入り、argumentsに対して暗黙的な変数宣言が行われます.
    var outer = '      ';
    function foo() {
        var inner = '      ,      ';
        console.log(outer);   
        //      outer, outer     foo   ,  outer  foo      
    }
    「自由変数のスコープは語法環境によって決定されます.つまり、そのスコープはコード書き段階で決定されています.コード実行段階で決定されるのではありません.」
    「自由変数の値はコード実行時に決定されます.変数変数変数の値は必ず変わるので、フリー変数の値はプログラム実行時にのみ決定されます.」
    包みを閉じる
    最初の文は環境を実行して、執行スタックは詳しい解をしました.忘れられた再復習ができます.実行スタックは、私たちが理解するクローズドの原理の基礎です.
    関数はスタックプロセスの図を呼び出してから干します.ついでに復習します.
    function foo () {
        function bar () {
            return 'I am bar';
        }
        return bar();
    }
    foo();
    関数呼び出し時にスタックに入り、呼び出しが完了しました.関数を実行すると、変数オブジェクトが関数の変数を格納する方法、パラメータargumentsなどを作成し、呼び出しが終了するとその変数オブジェクトは破棄されます.(理想的な場合は、好ましくない場合は「クローズド」コールが発生します).
    閉包とは何ですか
    クローズドとは、別の関数のスコープにアクセスする権限を持つ変数の関数です.
    ——「JavaScript高級プログラム設計」
    クローズドとは、自由変数にアクセスできる関数です.
    ——MDN
    クローズドの特徴は、まず関数です.次に、親レベルのスコープの変数オブジェクトにアクセスできます.親レベルの関数が呼び出しを完了しても、「スタックから廃棄すべき」です.
    クローズド発生判定
  • 関数は、パラメータ伝達
  • として機能します.
  • 関数は、戻り値伝達
  • として機能する.
    function foo() {
        var fooVal = '2019';
        var bar = function() {
            console.log(fooVal);    // bar         fooVal
        }
        return bar;                 //         
    }
    
    var getValue = foo();
    getValue();                     // 2019
    関数の中で誰が閉じているかについては、ドキュメントごとに解釈が異なります.ここでは、Chromeの方式に従い、とりあえずfooはクローズドです.
    作用領域と作用分域鎖規則の定義のために,サブ環境の自由変数は層毎に親環境に対してのみ検索できる.
    しかし、クローズドすることにより、外部環境でも変数fooValを取得することができます.foo()関数の実行は完了しましたが、関数コールスタックからは破壊されていません.変数オブジェクトの格納はまだアクセスできます.
    実際の実行過程は図を見てください.
    上記のコードを以下に変えて、次を見ます.
    function foo() {
     var fooVal = '2019';
     var bar = function() {
     console.log(fooVal);     // bar         fooVal
     }
     return bar;              //         
    }
    var getValue = foo();
    var fooVal = '2018';      //    fooVal         
    getValue();               // 2019
    答えと結果が一致しない仲間は後で自由変数を理解します.「自由変数のスコープは、コードの作成時に決定される」ので、関数getValue()が使用するfooValは、大域的なスコープではなく、fooのスコープで使用される.
    正解した子供たちはもう一つ問題を出して、あなたの記憶を深めます.
    function fn() {
        var max = 10;
        function bar(x) {
            if (x > max) {    
                console.log(x)
            }
        }
        return bar;
    }
    var f1 = fn();
    var max = 100;
    
    f1(20);    //   20
    関数maxにおける自由変数としてbarが作成したときに、関数barfnが作成されたので、その作用領域チェーンはmaxで終了して戻ってきました.
    注:スタックに格納されているのは、クローズドに使用される自由変数だけではなく、親レベル関数の変数オブジェクト全体(親レベル関数のスコープで宣言されている方法、変数、パラメータなど)です.
    クローズドのアプリケーションシーン
    上記では、閉包の特徴を述べましたが、私たちが作用領域をまたいで値を取ることができる(親子の役割領域に限らない)ということです.実際に開発された栗を二つ挙げます.
  • カプセル化反転保存機能領域
  • for(var i = 1; i < 5; i++) {
        setTimeout((function(i){
           return function() {
               console.log(i);        
           } 
        })(i), i * 1000)
    }
    //   :         i,        (  )   i,             
  • プライベート変数と方法のモジュール化を実現する
  • var makePeople = function () {
        var _name = '    ';
        return {
            getName: function () {
                console.log(_name);
            },
            setName: function (name) {
                if (name != 'Hello world') {
                    _name = name;
                }
            }
        }
    }
    
    var me = makePeople();
    me.getName();                   // '    '
    me.setName('KenTsang');         
    me.getName();                   // 'KenTsang'
    
    //   :    _name        ,               ,       
    クローズドの応用シーンはまだたくさんあります.具体的には具体的な分析が必要です.
    クローズドによるメモリ漏れ
    クローズド・パケットの使用は関数のアンロック過程を破壊した.スタックを解釈すると、同じ関数を呼び出しても、作成した変数オブジェクトは同じではなく、メモリはそれぞれ独立しています.
    スタックには入れられないだけで、関数の変数オブジェクトは有効に回収されていません.ブラウザのメモリ占有率が徐々に増加し、メモリ占有が高すぎると、ページの詰まりを引き起こし、ブラウザが崩壊することもあります.これは私たちがよくクローズドを使いすぎると「メモリリーク」と言います.
    ですから、合格した前の方は、クローズド以外にも、正しいキャンセルが必要です.ごみ回収メカニズムについて説明する場合、変数値をfnに設定することで変数の参照を解除し、次のゴミを回収して廃棄することができます.
    function foo() {
     var fooVal = '2019';
     var bar = function() {
     console.log(fooVal);     
     }
     return bar;              
    }
    var getValue = foo();
    var fooVal = '2018';     
    getValue();
    getValue = null;         //     ,            
    締めくくりに書く
    クローズドバックは、フロントエンド初心者の難点として、説明するのは容易ではないです.スコープに関連して、コンテキスト環境、変数オブジェクトなどを実行します.
    知識の纏めは、シリーズの更なる文の原点である.
    知識は小ネタではなく、笑いを聞いたら忘れます.ただ体系を形成して閉ループを達成してこそ、記憶に深く入ることができます.
    参考文献:
  • Javascriptプロトタイプとクローズド
  • を深く理解する.
    本記事の第1弾Githubは、Starを期待しています.https://github.com/ZengLingYong/blog
    作者:以楽之名
    本文はオリジナルで、不適切なところがあります.ご指摘ください.転載は出典を明示してください.