私は最近ES2015の練習を見ています、つまり言うことがあります
JavaScriptにはブロックレベルの範囲はありません
あなたはこの問題を理解していないかもしれません、最初に例を見てみましょう
var a = [] for(var i = 0; i <10; i ++){a [i] = function(){console.log(i); }} a [6]();多くの人がこの質問の結果は6だと思うと思いますが、残念なことに、答えは10です。何か他のものを試してください。 a [7]()、a [8]()、およびa [8]()はすべて10 !!
JSは多くの場合、処理時にプリミティブ変数を対応するオブジェクトに包みます。たとえば、var str = "hello world"; str.slice(1)の場合。
JSの実際のプロセスは、おそらくvar str = "hello world"; new String(str).slice(1)です。このようなプロセスは、問題を理解するのに問題を引き起こす可能性があります。
ここでこの問題を説明するために、私はプリミティブタイプの数値タイプに属し、それを数値タイプとして明示的に宣言します。基本タイプの割り当てプロセスは、メモリを再適用して変数の方向を変更することであるため、新しい数字オブジェクトのプロセスを使用してこのプロセスをシミュレートします。変更されたコードは次のとおりです。
var a = [] var i = new Number(0); }} a [6](); // 10a [7](); // 10a [8](); // 10a [9](); // 10a [9](); // 10
これらの変数の相対的なメモリアドレスをプログラムと組み合わせて見てみましょう。
(function(){var id = 0; function generateid(){return id ++;}; object.prototype.id = function(){var newid = generateid(); this.id = function(){return newid;}; return newid;};}新しい番号(i+1)、i.id()){a [i] = function(){console.log(i.id()); console.log(i.tostring()); }} a [6](); // 10 10a [7](); // 10 10a [8](); // 10 10a [9](); // 10 10console.log(i.id())// 10ここでは、iの「割り当て」効果全体をシミュレートしました。Iの相対アドレスは0から10に変更されました(最終的には、ループから飛び出す前に1回追加する必要があります)。
Iの相対アドレスを見ると、問題が見つかりました。[x](x:0〜9)に対応する関数が実行されると、参照されるiの相対アドレスは10です。
ここでは、ブロックレベルの範囲の問題を伴います。ここでは、ES6の紹介でRuan Yifengからの一節を引用します。
ES5には、グローバルな範囲と機能スコープのみがありますが、ブロックレベルのスコープはありません。
ES5は、JSの最も広く使用されているバージョンです。この文には、JavaScriptにはブロックスコープはないと述べています。グローバルな範囲とブロックレベルの範囲のみがあります。
理解する方法は?例えば
for(var i = 0; i <10; i ++){console.log(i);} console.log(i); // 10console.log(window.i); // 10直感的には、forループはコードブロックであり、ブロックレベルのスコープに属する必要があると考えています。ただし、正常に0から9を出力できるだけでなく、forループに10を外部から出力することもできます。同時に、forループでiを定義しましたが、私はグローバルウィンドウオブジェクトにぶら下がっているようです(それがnodejsの実行環境である場合、それはグローバルオブジェクトにぶら下がっています)
したがって、JavaScriptのループのようなブロックは、ブロックレベルのスコープの効果を持ちません。ループのようなコードブロックの変数を定義することは、現在の範囲で変数を直接定義することと変わりません。
しかし、機能を通じてスコープを分離することができます。
(function(){for(var i = 0; i <10; i ++){console.log(i);} console.log(i);})()console.log(i); //// iは定義されていません同時に、console.log(window.i)の場合、実行されると、未定義の結果が取得されます。ここでは、即時実行関数を使用してスコープを形成します。コードブロックと同様の役割を果たします。この関数範囲の後、私がアクセスできなくなりました。ただし、関数範囲内でいつでもアクセスできます。
前の質問に戻ると、JavaScriptのグローバルスコープとブロックレベルの範囲のみと組み合わせて理解します。 for loopでは、Iを定義したIを現在のスコープ、つまりウィンドウスコープで定義する必要があります。ループ本体では、[i]に関数を割り当てます。この関数を実行すると、状況は次のとおりです。
私は関数には存在しないので、スコープチェーンに従ってiを見つけます。この時点で出力されるのは、これです。私はループの最後の+1から飛び出すので、私は10になるので、出力の結果は常に10になります。しかし、私たちが本当に必要とするのは最後のIではなく、私は中間プロセスです。この問題を解決したい場合は、変数Iを脇に置く必要があります(最後のIは必然的に10になるため)。 [0]に対応する関数を値0に参照し、[1]に対応する関数を値1を参照させる必要があります。
以前のコードに戻ります。
図の矢印は、I(0〜9)にアクセスできることを示しています。ここでは、forループは単独でブロックレベルのスコープを形成しないため、スコープチェーンに従うときにforループで定義されたiにアクセスします。
ここでは、範囲を形成できる即時の実行関数でコードをラップし、それの値を渡します。次の:
var a = [] var i = new number(0); console.log(i.id()); // 0for(; i <10; i = new Number(i+1)、i.id())){(function(i){console.log(i.id()); console.log(i.tostring(); a [6); // 6 6a [7](); // 7 7a [8](); // 8 8a [9](); // 9 9console.log(i.id()); // 10}この即時実行関数は数値値0〜9を指しているため、関数a [i]を実行すると、最初にスコープチェーンに沿った即時実行関数の範囲が見つかります。即時実行関数は0〜9から数値参照を維持し、関数A [i]のIの値を正しく出力できます。実行結果を通じて、実行結果が正しいだけでなく、参照される値の相対的なメモリアドレスも正しいことがわかります。次に、テストのために元々明示的に宣言されていた数値オブジェクトを変更します。次のように:
var a = []; for(var i = 0; i <10; i ++){(function(i){a [i] = function(){console.log(i);}})(i);}最後に、varの代わりにletを使用して推奨されるES6構文を見てみましょう。
// es6 code var a = [] for(let i = 0; i <10; i ++){a [i] = function(){console.log(i); }} a [6](); // babel compiled and verated es5 code "sustict"; var a = []; var _loop = function _loop(i){a [i] = function(){console.log(i); };}; for(var i = 0; i <10; i ++){_loop(i);} a [6]();ソリューションがES6ソリューションに非常に似ているかどうかを見てみましょう。ここで、当面の実行関数は、生成されたES5コードで_loop関数と_loop(i)の実行と同等です。