概要:このチュートリアルでは、JavaScript let キーワードを使用してブロック スコープされた変数を宣言する方法を学べます。
JavaScript let キーワードの概要
ES5 では、var キーワードを使用して 変数を宣言する場合、変数のスコープはグローバルかローカルのいずれかになります。関数の外側で変数を宣言すると、変数のスコープはグローバルになります。関数の内側で変数を宣言すると、変数のスコープはローカルになります。
ES6 では、let キーワードを使用して変数を宣言する新しい方法が提供されています。let キーワードは var キーワードに似ていますが、これらの変数はブロック スコープになります。たとえば
let variable_name;Code language: JavaScript (javascript)JavaScript では、ブロックは中括弧 {} で示されます。たとえば、if else、 for、do while、while、try catch などがあります。
if(condition) {
// inside a block
}Code language: JavaScript (javascript)次の例を参照してください。
let x = 10;
if (x == 10) {
let x = 20;
console.log(x); // 20: reference x inside the block
}
console.log(x); // 10: reference at the begining of the scriptCode language: JavaScript (javascript)スクリプトの仕組み
- まず、変数
xを宣言し、その値を 10 に初期化します。 - 次に、内の同じ名前の変数
xを宣言する新しい変数を宣言しますifブロックですが、初期値は 20 です。 - 次に、変数
xの値を出力しますifブロックの内側と後側。
let キーワードはブロック スコープされた変数を宣言するため、if ブロック内の x 変数は **新しい変数** であり、スクリプトの先頭で宣言された x 変数をシャドウします。そのため、コンソール内の x の値は 20 になります。
JavaScript エンジンが if ブロックの実行を完了すると、if ブロック内の x 変数はスコープ外になります。したがって、if ブロックに続く x 変数の値は 10 になります。
JavaScript let とグローバル オブジェクト
var キーワードを使用してグローバル変数を宣言すると、その変数が グローバルオブジェクト のプロパティリストに追加されます。Web ブラウザの場合、グローバルオブジェクトは window です。たとえば
var a = 10;
console.log(window.a); // 10Code language: JavaScript (javascript)ただし、let キーワードを使用して変数を宣言すると、その変数はプロパティとしてグローバルオブジェクトに添付され **ません**。たとえば
let b = 20;
console.log(window.b); // undefinedCode language: JavaScript (javascript)JavaScript let と for ループ内のコールバック関数
次の例を参照してください。
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}Code language: JavaScript (javascript)コードの目的は、0 から 4 までの数字を 1 秒ごとにコンソールに出力することです。ただし、数字 5 を 5 回出力します。
5
5
5
5
5この例では、変数iはグローバル変数です。ループの後、その値は5になります。コールバック関数がsetTimeout()関数に渡されると、同じ変数iを参照して、値が5になります。
ES5では、各コールバック関数が新しい変を参照するように別のスコープを作成することでこの問題を解決できます。また、新しいスコープを作成するには、関数を作成する必要があります。通常、次の例のようにIIFEパターンを使用します。
for (var i = 0; i < 5; i++) {
(function (j) {
setTimeout(function () {
console.log(j);
}, 1000);
})(i);
}Code language: JavaScript (javascript)出力
0
1
2
3
4ES6では、letキーワードが各ループの反復で新しい変数を宣言します。したがって、問題を修正するには、varキーワードをletキーワードに置き換えるだけで済みます。
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000);
}Code language: JavaScript (javascript)コードを完全にES6スタイルにするには、次の例のようにアロー関数を使用できます。
for (let i = 0; i < 5; i++) {
setTimeout(() => console.log(i), 1000);
}Code language: JavaScript (javascript)後のチュートリアルでアロー関数の詳細について学習することに注意してください。
再宣言
varキーワードを使用すると、問題なく変数を再宣言できます。
var counter = 0;
var counter;
console.log(counter); // 0Code language: JavaScript (javascript)ただし、letキーワードを使用して変数を再宣言すると、エラーが発生します。
let counter = 0;
let counter;
console.log(counter);Code language: JavaScript (javascript)エラーメッセージを次に示します。
Uncaught SyntaxError: Identifier 'counter' has already been declaredCode language: JavaScript (javascript)JavaScriptの変数とホイスト
次の例を検討してみましょう。
{
console.log(counter); //
let counter = 10;
}Code language: JavaScript (javascript)このコードはエラーを引き起こします。
Uncaught ReferenceError: Cannot access 'counter' before initializationCode language: JavaScript (javascript)この例では、counter変数に宣言前にアクセスすることはReferenceErrorを引き起こします。letキーワードを使用した変数の宣言はホイスティングされないと考えるかもしれませんが、されます。
実際、JavaScriptエンジンはletキーワードで宣言された変数をブロックの先頭にホイスティングします。ただし、JavaScriptエンジンは変数を初期化しません。したがって、初期化されていない変数を参照するとReferenceErrorが発生します。
一時不活性ゾーン(TDZ)
letキーワードで宣言された変数には、一時不活性ゾーン(TDZ)と呼ばれるものがあります。TDZは、ブロックの開始から変数の宣言が処理されるまでの時間です。
次の例は、TDZが場所ベースではなく時間ベースであることを示しています。
{ // enter new scope, TDZ starts
let log = function () {
console.log(message); // messagedeclared later
};
// This is the TDZ and accessing log
// would cause a ReferenceError
let message= 'Hello'; // TDZ ends
log(); // called outside TDZ
}Code language: JavaScript (javascript)この例では
最初に、波括弧が新しいブロックスコープを開始するため、TDZが開始されます。
次に、log()関数式はmessage変数にアクセスします。ただし、log()関数はまだ実行されていません。
次に、message変数を宣言して、その値を'Hello'に初期化します。ブロックスコープの開始からmessage変数にアクセスするまでの時間を一時不活性ゾーンと呼びます。JavaScriptエンジンが宣言を処理すると、TDZは終了します。
最後に、TDZの外側でmessage変数にアクセスするlog()関数を呼び出します。
次の例で示すように、TDZ内でletキーワードで宣言された変数にアクセスするとReferenceErrorが発生することに注意してください。
{ // TDZ starts
console.log(typeof myVar); // undefined
console.log(typeof message); // ReferenceError
let message; // TDZ ends
}Code language: JavaScript (javascript)myVar変数は存在しない変数であるため、その型はundefinedです。
一時不活性ゾーンは、宣言前に変数を誤って参照するのを防ぎます。
まとめ
letキーワードを使用して宣言された変数はブロックスコープになり、値に初期化されず、グローバルオブジェクトにアタッチされません。letキーワードを使用して変数を再宣言すると、エラーが発生します。letキーワードを使用して宣言された変数のTDZは、初期化が評価されるまでブロックから開始されます。