概要:このチュートリアルでは、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 script
Code 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); // 10
Code language: JavaScript (javascript)
ただし、let
キーワードを使用して変数を宣言すると、その変数はプロパティとしてグローバルオブジェクトに添付され **ません**。たとえば
let b = 20;
console.log(window.b); // undefined
Code 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
4
ES6では、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); // 0
Code language: JavaScript (javascript)
ただし、let
キーワードを使用して変数を再宣言すると、エラーが発生します。
let counter = 0;
let counter;
console.log(counter);
Code language: JavaScript (javascript)
エラーメッセージを次に示します。
Uncaught SyntaxError: Identifier 'counter' has already been declared
Code language: JavaScript (javascript)
JavaScriptの変数とホイスト
次の例を検討してみましょう。
{
console.log(counter); //
let counter = 10;
}
Code language: JavaScript (javascript)
このコードはエラーを引き起こします。
Uncaught ReferenceError: Cannot access 'counter' before initialization
Code 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は、初期化が評価されるまでブロックから開始されます。