JavaScriptにおける実行環境、スコープおよび変数の向上(hoisting)

2162 ワード

まず次のコードを見てください.
var a = 0;
alert("1st alert : a = " + a);
function fun(){
    alert("2nd alert : a = " + a);
    var a = 1;
    setTimeout(function(){
        alert("3rd alert : a = " + a);
        a = 2;
    },1000);
    a = 3;
    setTimeout(function(){
        alert("4th alert : a = " + a);
        a = 4;
    },4000);
}
fun();
alert("5th alert : a = " + a);
コード実行の結果は:
1 st alert:a=0
2 nd alert:a=undefined
5 th alert:a=0
3 rd alert:a=3
4 th alert:a=2
疑問1:2 nd alertにとって、なぜaの値はundefinedですか? 
まず、JSの実行環境とスコープを見ます.
実行環境(executing context)は、変数または関数がアクセスできる他のデータを定義しています.JSには二つの実行環境があります.一つはグローバル環境、つまりWebブラウザのwindowオブジェクト、もう一つは関数の実行環境です.
実行環境には変数オブジェクトがあります.このオブジェクトは環境で定義されたすべての変数と関数を保存します.
コードが実行されると、変数オブジェクトの作用ドメインチェーンが作成されます.作用ドメインチェーンの先端は現在の環境の変数オブジェクトです.次に、外層環境の変数オブジェクトです.
識別子の解析プロセスでは、フロントエンドから、識別子が見つかるまで、または完全局環境が検索されるまで、スコープのレベル1に沿って検索が開始される.
したがって、JSでは、括弧は独立した作用領域を表していません.循環体で定義された変数は、体外に循環してもアクセスできます.
次の例を見てください
var a = 0;
function fun(){
    alert("a=" + a);
}
fun();
結果はa=0です
これは、fun関数を呼び出すと、検索識別子aは、本実行環境では見つけることができないが、ドメインチェーンを介して外層作用領域を検索すると、aが見つかったからである.
なぜ最初の例では、2 nd alert、aの値はundefinedですか?
これは、JSにおいて、var宣言を使用する変数または関数宣言方式を使用して宣言する関数(関数式ではない)が、最も近い環境に自動的に追加され、いわゆる変数アップグレード(Hoisting)が行われるからです.上のfun関数の定義に相当する前の2行のコードが次のようになります.
function fun(){
    var a;
    alert("2nd alert : a = " + a);
    a = 1;
    //other codes
}
したがって、識別子aを検索する際には、外部環境に検索することなく、本実行環境で検索することができるので、2 nd alertではaの値はundefinedである.
関数の定義についても同じです.これはなぜ関数宣言の方式を使う時に、関数宣言の前に関数宣言を入れることができますが、関数表現を使う時にはできない原因です.
質問2:5 th alertはなぜ3 rdと4 thの前にあるのですか?
これはJavaScriptエンジンがジョブキューに対して単一スレッドで処理しているためであり、set Timeoutは非同期コードであり、JSスレッドに同期コードがない場合にのみ非同期コードが実行されるからである.
setTimeout(function(){while(true){}},1000);
setTimeout(function(){alert('end 2');},2000);
setTimeout(function(){alert('end 1');},100);
alert('end');
ですから、上のコードの中で、まずendが現れて、次にend 1が続きます.その後はもう現れません.最初のsetTimeout関数では、デッドサイクルがJSエンジンの単一スレッドを占有しているため、他のプロセスがブロックされている.