JavaScriptにおけるスコープとクローズド

4976 ワード

JavaScriptには重要で重要な概念があります.大事なことを三回言いなさい.黒板をたたいて、居眠りしている学生は目を覚まして、下の話は重点です!
多くのベテランドライバーがこの場所で転覆する可能性があるという.ここでは若い運転手が運転を再開します.必要な乗客はカードで乗車します.
前に変数の昇格と作用域に関する文章が書いてありましたが、食事の味に合わせてもっと良くなってきました.本稿では、機能領域に関する知識を過剰に説明しなくなりました.
実行環境(execution context)及び作用ドメインチェーン(scope chain)
実行環境はJavaScriptの中で最も重要な概念です.一つもないです.実行環境は、変数や関数がアクセスできる他のデータを定義し、それぞれの挙動を決定します.各実行環境には、関連する変数オブジェクトがあり、環境で定義されているすべての変数と関数がこのオブジェクトに保存されます.私たちが自分で作成したコードはこのオブジェクトにアクセスできませんが、データを処理する際にはその背景で解析器が使用されます.
グローバル実行環境
グローバル実行環境は、最も周辺的な実行環境である.JavaScriptによって、宿主環境の違いを実現し、実行環境を表す対象も違っています.Webブラウザでは、グローバルの実行環境はwindowオブジェクトです.だから、一番外側に変数や関数を宣言したら、自動的にwindowオブジェクトの下の属性や方法になります.ある実行環境のコードは実行後に破壊されるはずです.その中のすべての変数と方法は破壊されます.しかし、グローバル実行環境は、プログラムが終了する(ウェブページやブラウザを閉じる)まで破壊されます.
実行環境
筆者もなぜ関数の実行環境は局所実行環境と呼ばないのか、あるいは関数実行環境と呼ばれるのか、大域実行環境と関数のみに分けられているのか疑問です.皆さんはちゃんと呼びますよね.
各関数は自分の実行環境を持っています.実行ストリームが関数に入ると、関数の環境が環境スタックに押し込まれます.関数実行後、スタックは環境をイジェクトし、制御権を前の実行環境に戻す.JavaScriptプログラムの実行フローはまさにこのメカニズムによって制御されています.ここに少しの像のスタックの方法があって、みんなは頭の中で倉庫の方法を少し補充することができて、後で先に出ます.
スコープチェーン
コードが環境で実行されると、変数オブジェクトのスコープが作成されます.作用するドメインチェーンの用途は、実行環境にアクセスできるすべての変数と関数に対する規則的なアクセスを保証することである.スコープの先端は、常に現在実行されているコードがある環境の変数オブジェクトです.この環境ポテンシャル関数であれば、その活動対象を変数オブジェクトとします.アクティブオブジェクトは最初に一つの内部変数だけを含んでいます.即ちargumentsオブジェクトです.作用領域の次の変数オブジェクトは、(外部)環境を含むものから来ています.次の変数は、もう一つの環境を含むものから、グローバルな実行環境まで延びています.グローバル実行環境は、ドメインチェーンの最後のオブジェクトとして機能します.
まるで、仮にホテル(変数)を探して宿泊したいとしたら、最初はきっと周辺百メートルのホテル(関数の実行環境)を探していましたが、一度周囲百メートル以内にホテルが見つからなかったら、捜索範囲を拡大して、方円一公里以内のすべてのホテル(次は環境を含む)を探して、都市全体を探します.最終的に私たちが探しているホテルを見つけました.
話がもっと多くても、コードで来たのは事実です.
var first = "first";
function first() {
    var second = "second";
    function second() {
        var third = "third";
        //        first 、second、third
    }
    //         first、second
}
//         first
以上のコードには三つの環境があります.一つはグローバル環境、もう一つはfirst()ローカル環境とsecond()ローカル環境です.それらのアクセス権限はすでに注釈に表記されています.内部環境は作用ドメインチェーンを介してすべての外部環境にアクセスできますが、外部環境は内部環境内の任意の変数と関数にアクセスできません.これらの環境間の連絡は直線的で順序的である.
でも、この中には小さな穴があります.ここまで言うなら、ついでに言ってください.新米はまだ踏んだことがないと思いますが、ベテランの運転手さんはもう会ったと思います.上記のコードを書く時、変数thirdの前にvarを追加するのを忘れたとしても、関数内変数の前にvarがなかったら、この変数は自動的にグローバル変数になります.この言い方は間違いないですが、次のコードを見ます.
var name = "first";
function first() {
    var name = "second";
    function second() {
        name = "third";
        console.log(name);
    }
    second();
    console.log(name);
}
first();
console.log(name);
上記のコードには、3つの名前がnameという変数があります.それぞれ3つの環境において、変数には作用領域があることを知っています.正常な3つの同名変数であれば、局所的に全体の値をカバーし、通常はthird、second、firstを出力するべきです.推定出力の結果は、third、second、thirdですか?グローバル変数を宣言したからですか?
そう言った以上、当然の結果はそうではないです.出力結果はthird、third、firstです.なんですか?グローバル変数nameは変更されていませんか?もちろん、日常的な使用では、このような状況に遭遇することはほとんど不可能です.まず、私たちは規範に合わない目でこの状況を見ましょう.
私たちは関数のスコープ内で変数がvarを使用していないと宣言した場合、グローバル変数が見つかるまでは、外部環境に行って同じ名前の変数を探します.この属性が見つからない場合は、グローバル変数として宣言します.しかし、同じ名前の変数が見つかったら、その値は同じ名前の変数に割り当てられます.上記の場合も同様であり、関数nameが同名の変数を発見した場合には、varvarに値を付与し、同時に検索を停止してしまうので、グローバル変数のFirstには影響がない.
スコープの延長
実行環境のタイプは全部で二つしかないですが、作用ドメインチェーンを延長する他の方法があります.このように、ドメインチェーンの先端に変数オブジェクトを一時的に追加すると、コード実行後に削除されます.具体的には、実行ストリームが次のいずれかのステートメントに入ると、スコープは長くなります.
次の二つの場合にはこのような現象が発生します.
  • try-catch文のcatchブロック
  • with文
  • この2つのステートメントは、スコープの先端に変数オブジェクトを追加します.
  • は、First文において、作成されたオブジェクトを作用領域チェーンに追加する.
  • は、name文に対して、ドロップされたエラーオブジェクトの宣言を含む新しい変数オブジェクトを作成します.
  • ここではこれ以上説明しない.
    包みを閉じる
    多くのベテランドライバーは匿名関数とクローズドという二つの概念をよく理解できないそうです.実は、クローズドとは別の関数のスコープ内の変数にアクセスできる関数です.閉じたパケットを作成する最も簡単な方法は、関数の内部に別の関数を作成することです.
    しかし、私達がもっと多い時の表現は、一つの関数が別の関数の変数の関数を呼び出して、私達はクローズドと呼びます.なにしろ、一つの関数の内部の関数が、外部の関数の値を使っていないと、その外部に一体関数があるのかどうかという関係は、自力更生です.
    クローズドと変数
    作用するドメインチェーンの構成機構は,関数内の任意の変数を含む最後の値しか得られないという注意すべき副作用を引き出した.
    
        
        
        
        
            var btns = document.getElementsByTagName("button");
            for(var i = 0; i < btns.length; i++){
                btns[i].onclick = function() {
                    alert("   " + i + "   ");
                }
            }
        
    
    
    これは循環を利用してボタンに値を割り当てる古典的な例である.私たちはボタンをクリックする時に、最初のボタンをクリックして「私は0番目のボタンです」とヒントを与え、2番目のボタンをクリックして「私は1番目のボタンです」とヒントを与えることを想定しています.しかし、実際に運転していると、どのボタンを押しても、「私は3番目のボタンです.」このとき、各関数は、変数nameを保存する同じ変数オブジェクトを参照しているので、各関数の内部withの値は3である.他の匿名関数を作成することによって強制的に閉じられた挙動を予想通りにすることができます.
    var btns = document.getElementsByTagName("button");
    for(var i = 0; i < btns.length; i++){
        btns[i].onclick = (function(num){
            return function() {
                alert("   " + num + "   ");
            }
        })(i);
    }
    
    参考文献:JavaScript高級プログラム設計(第三版)