JavaScript-スコープと「クローズド」

5541 ワード

独り言を続ける.
今日はJavaScriptの役割領域と「閉包」について話したいです.もちろん、私の個人的な特徴を持っています.
スコープ
JavaScriptによると、私は一つのスコープしかないと知っています.「関数スコープ」といいます.まず栗を見ます.
var a = "a-out";
(function () {
    console.log(a);
    var a = "a-in";
})();
上の匿名関数が実行されたら、何を出力すると思いますか?
実際にやってみたら、undefinedということになります.これはゆっくり話しましょう.
まず、JavaScriptには「ブロックレベルスコープ」がありません.かっこで「局所変数」を定義することはできません.すべての変数のスコープは関数レベル、すなわち関数内部の任意の場所で宣言された変数は、関数内の任意の位置で使用できます.
もちろん、例外としては、どの関数の内部にもない変数が「グローバル変数」になります.同様に、関数内部では、var宣言を忘れて直接使用する変数も大域変数となりますので、関数内部で使用する変数は、問題を避けるために、頭に明示的に宣言されることが多いです.
振り返って上の栗:
  • の第1行は、グローバル変数aを宣言し、その後に割り当てられた.
  • 匿名関数の第1行は、関数内部がaを宣言しているので、内部変数aの値をコンソールに出力する.しかし、内部変数aに対する割当値は現在の行の後にあり、現在の変数には値がないので、aが出力される.
  • の匿名関数の第二行は、内部変数undefinedを宣言し、その後、割当される.
  • このことについて私の理解:
    JavaScriptは「関数スコープ」しかないので、スクリプトの解釈器は先に関数内部をスキャンして、すべての宣言の変数を識別して記録します.そして、実行関数のこの段階で変数名が発生したら、まず関数内部の変数リストを検索して、その内部変数の現在の値を現在の語句に渡します.
    関数の内部に変数が使用されている場合、その変数は関数内で宣言されていませんか?
    上の栗を直します.
    var a = "a-out";
    (function () {
        console.log(a);
        // var a = "a-in";
    })();
    
    試してみると、ここの匿名関数はaでプリントされていることが分かります.ですから、関数内でこの変数を宣言しないと、関数の外部から探しに来ます.マルチレベルネストの関数については、同じ名前の変数が見つかるまで、または「トップレベル」まで見つけられないまま停止します.(この場合、実行関数が間違っていたら、プロンプト変数が定義されていません).
    もちろん、この変数を検索する過程は直観ではなく、私の説明だけです.反対ではなく、理解してもらえますように.
    関数のパラメータについても、関数の内部変数と見なしますが、その値は関数が実行されるまでは確定できません.
    関数の定義と関数の実行を分けて見ます.
    関数の定義は、静的な環境で関数の内外の環境が固定されています.多くの変数の値はまだ変動していますが、実行するたびに決定されることもありますが、これは私たちが「引用」するのを妨げていません.関数の実行中、各具体的な参照は変数の位置に、変数のその時の値によって置き換えられます.
    特に、関数定義段階(または実行関数ではなく解釈器解析の場合)では、どの変数が使用できるかも決定されます.例えば、関数内部で"a-out"変数が宣言されている場合、関数内部でa変数に使用されるステートメントは、このaに関連付けられている.内部にない場合は、外部作用域(外部関数の作用域)で検索しますが、まだない場合は?関数を実行する時に間違えましたよね.
    この話を続けて、私達は「閉包」を引き出します.
    包みを閉じる
    「閉包」という厳粛なものについては、やはりウィキペディアの定義を見てみましょう.
    コンピュータ科学では、クローズドはクローズドの略称であり、自由変数を引用した関数である.この参照された自由変数はこの関数と共に存在し、作成された環境から離れても例外ではない.したがって、もう一つの説では、クローズドは関数とその関連する参照環境とを組み合わせたエンティティであると考えられている.クローズドパケットは、実行中に複数のインスタンスがあり得るが、異なる参照環境と同じ関数の組み合わせは、異なるインスタンスを生成することができる.
    栗を見に来ました.
    var count = (function () {
        var i = 0;
        return function () {
            return ++i;
        };
    })();
    
    count(); // 1
    count(); // 2
    
    この栗には匿名関数が二つあります.一つは別の内部に嵌められています.外部層の匿名関数は直ちに実行され(aによって)、次いで内部の匿名関数が戻り値として変数countに割り当てられる.したがって、上記のプロセスを経て、countの値は関数オブジェクトです.
    countに関連するこの関数は特別であり、定義時には外部関数の変数(function () {})()を参照しているので、外部変数iとこの関数は上記の定義の「クローズド」を構成しており、上記のような現象が発生している.
    このすべては、この不気味な名前と一緒に、何の効果がありますか?人を惑わせたり、慣れない人に「NB」と怒鳴らせたりするだけですか?
    クローズドはJavaScriptでローカル変数を使用するメカニズムを提供するためであるという説を見たことがあります.これがクローズドの評価そのものが正しいかどうかはともかく、「局所変数」というものを理解してみよう.やはり栗を挙げます.
    function Person(name) {
        this.name = name;
        this.getName = function () {
            return this.name;
        };
    }
    
    もし他の人がこのi類(とりあえず「類」といいます.後は専門的にJavaScriptの継承とクラスについて話したいです.)を使う場合、Personを通してgetName()を取得するしかないです.nameに直接アクセスして値を変えることができないと、現実的ではないと思います.JavaScriptはprvate、publicのようなものを提供していないので、すべてのものはデフォルトで公開されています.もしそうしなければならないならば、結局このようにするのも合理的な応用のシーンを持っているので、通常はクローズドによって厳格に実現することができます.
    function Person(name) {
        var _name = name;
        this.getName = function () {
            return _name;
        };
    }
    
    もちろん、前述したように、関数のパラメータを内部変数と見なすこともできます.
    function Person(name) {
        this.getName = function () {
            return name;
        };
    }
    
    私たちはこのクラスを使います.
    var me = new Person("luobo");
    me.getName(); // "luobo"
    
    明らかに、name属性がないと、直接にこの値を修正することができません.(もちろん、「プライベート」のメンバーとして使用するのが対象であれば、上記の方法を採用しても、戻ってくるのは対象そのものですので、対象を変更することができます.)
    上の問題に戻ります.クローズドの役割については、やはり「クローズド」は言語そのものが提供する仕組みであり、必ずしも特定の目的のために作られたものではなく、「局所変数」を作るためのものではないと思います.自分の必要に応じて、適当なところで使えばいいです.本当に使えるといいです.
    結び目
    すみません、今日は気分がよくなくて、ものを書くのはあまり激情がないので、上の文字は確かに気を使いましたが、自分でも満足できませんでした.作用域と「クローズド」は私が理解しているJavaScriptの中の重要なテーマです.長い時間をかけて、上記のような体験ができました.
    作用域については、まずJavaScriptコードの実行についてある程度理解する必要があると思います.(私の理解を話してみます.交流を歓迎します.)ブラウザの環境下では、関数の内部に書かれているステートメントがないまま実行されました.関数の内部に書かれたコードは、関数が呼び出されるまで待つしかありません.しかし、ブラウザには実行関数がありませんが、まず関数を「読み」、「理解」してから記録します.このように、いつでもこの関数を使う必要がある時は、直接に持ってきて、その時の環境に合わせて実行します.
    この過程の細部には、作用域、閉鎖に関する影があります.
    もちろん上は私の個人的な理解です.説明も正確ではありません.しかし、関数の定義と実行のメカニズムについて、より深く理解する必要があると思います.特に、JavaScriptという言語についてもっと深く理解したいと思います.もちろん、私も頑張ります.
    「閉包」については、本当に「高級」な話題です.また、閉パックの原理を理解していない場合は、知らず知らずのうちに自分の穴を掘ってやりました.
    var arr = ['a', 'b', 'c'], funcs = [];
    for (var i = 0, len = arr.length; i < len; i++) {
        funcs[i] = function () {
            return arr[i];
        };
    }
    
    この造作されたクリの中で、私の意図は、配列_nameを得ることです.配列の各項目は、nameに対応する位置に戻る値の関数です.ちょっと回りくどいですが、どういう意味か分かりますか?しかし、結果はそうではない.
    funcs[1](); // undefined
    
    おかしいですね.funcs、つまりarrに戻るべきではないですか?
    そのために悩んだことがありますが、後になって気づいたのです.私はいつのまにかクローズドで自分の穴を掘ってしまいました.
    まだ読んでいないなら(もちろん、私の話自体は乱れています.あなたのために難しいです.)ヒントを与えます.
  • は、コンソールで変数arr[1]を出力してみて、現在の値
  • を見てください.
  • は次いで、コンソールから'b'を出力し、現在の値
  • を見る.
  • forサイクルにおいて、実際には構築される匿名関数毎に、i
  • が返される.
  • は、arr[i]を介して変数arr[i]の値を1
  • に変更する.
  • 上のi = 1を実行してください.またはi funcs[1]()も同じです.結果を見ればわかるはずです.
  • はい、これは閉包です.