let 文はブロックスコープの局所変数を宣言します。任意で値を代入して初期化できます。
The source for this interactive example is stored in a GitHub repository. If you'd like to contribute to the interactive examples project, please clone https://github.com/mdn/interactive-examples and send us a pull request.
構文
let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN]];
引数
var1,var2, …,varN- 変数の名前。任意の有効な識別子を指定できます。
value1,value2, …,valueNOptional- 変数の初期値。任意の有効な式を指定できます。
説明
let を使用することで、変数のスコープをそれが使用されたブロック、文または式に限定することができます。これは var キーワードとは異なり、グローバル変数を定義したり、ブロックスコープに留まらない関数全体でのローカル変数を定義したりしません。その他の var と let の違いは後者がパーサーが評価したときにのみ value に初期化されます(下記参照)。
const と同様に、let はグローバル(一番上のスコープ)で宣言されたときに window オブジェクトのプロパティを生成しません。
なぜ "let" という名前が選ばれたのかについては、こちら で解説されています。
スコーピングのルール
let で定義された変数では、自身が定義されたブロックがスコープになります。そのブロックに含まれている全てのサブブロックでも同様です。この点において let のふるまいは var にとてもよく似ています。主に違うのは、var で定義された変数のスコープはそれを含んでいる関数全体になるということです。次のコードはその例です。
function varTest() {
var x = 31;
if (true) {
var x = 71; // 同じ変数です!
console.log(x); // 71
}
console.log(x); // 71
}
function letTest() {
let x = 31;
if (true) {
let x = 71; // 異なる変数
console.log(x); // 71
}
console.log(x); // 31
}
var とは異なり、プログラムのトップレベルと関数で、グローバルオブジェクト上にプロパティを生成しません。たとえば:
var x = 'global'; let y = 'global'; console.log(this.x); // "global" console.log(this.y); // undefined
プライベートメンバーのエミュレート
constructor で処理することで、クロージャを使用することなくプライベートメンバーを生成するために let ステートメントを使用できます:
var Thing;
{
let privateScope = new WeakMap();
let counter = 0;
Thing = function() {
this.someProperty = 'foo';
privateScope.set(this, {
hidden: ++counter,
});
};
Thing.prototype.showPublic = function() {
return this.someProperty;
};
Thing.prototype.showPrivate = function() {
return privateScope.get(this).hidden;
};
}
console.log(typeof privateScope);
// "undefined"
var thing = new Thing();
console.log(thing);
// Thing {someProperty: "foo"}
thing.showPublic();
// "foo"
thing.showPrivate();
// 1
局所変数をクロージャで閉じた場合と同様に、 var を使ってプライバシーパターンを作成できますが、上の例のようなブロックスコープではなく、関数スコープ(通常はモジュールパターンのIIFE)が必要です。
再宣言
同じ関数かブロックスコープで同じ変数を再宣言すると SyntaxError が発生します。
if (x) {
let foo;
let foo; // SyntaxError が投げられます。
}
switch 文には 1 つのブロックしかないため、エラーを発生させてしまうかもしれません。
let x = 1;
switch(x) {
case 0:
let foo;
break;
case 1:
let foo; // 再宣言によって TypeError
break;
}
ですが、case 節の中にネストされたブロックは、新しいブロックスコープのレキシカル環境をつくるように指示します(それは重要です)。それは上記の再宣言エラーを発生させないでしょう。
let x = 1;
switch(x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break;
}
}
Temporal dead zone
undefined の値で始まる var 変数と異なり、 let 変数は定義が評価されるまで初期化されません。変数を宣言より前で参照することは ReferenceError を引き起こします。ブロックの始めから変数宣言が実行されるまで、変数は "temporal dead zone" の中にいるのです。
function do_something() {
console.log(bar); // undefined
console.log(foo); // ReferenceError
var bar = 1;
let foo = 2;
}
Temporal dead zone と typeof
単純に宣言されていない変数や undefined の値を保持する変数とは異なり、 TDZ内の変数の型を確認するために typeof 演算子を使用すると、ReferenceError がスローされます:
// 'undefined' が表示されます console.log(typeof undeclaredVariable); // 'ReferenceError' が発生します console.log(typeof i); let i = 10;
レキシカルスコープと組み合わせた Temporal dead zone の例
レキシカルスコープにより、式 (foo + 55) 内の識別子 "foo" は、if ブロックの foo に評価され、その上にある 33 の値を持つ変数 foo には評価されません。
その行では、if ブロックの "foo" はすでにレキシカル環境で作成されていますが、未だ(ステートメントそのものの一部である)その初期化に到達・終了していないので、その foo はまだ Temporal dead zone 内にあります。
function test(){
var foo = 33;
if (true) {
let foo = (foo + 55); // ReferenceError
}
}
この現象は、次のような状況であなたを混乱させる可能性があります。let n of n.a 命令はfor ループブロックのプライベートスコープにすでに存在し、したがって、識別子 "n.a" は、命令自体の最初の部分にある 'n' オブジェクト ("let n") のプロパティ 'a' に解決されます。その宣言ステートメントに到達・終了していないので、その n 及び n.a はまだ Temporal dead zone 内にあります。
function go(n) {
// n here is defined!
console.log(n); // Object {a: [1,2,3]}
for (let n of n.a) { // ReferenceError
console.log(n);
}
}
go({a: [1, 2, 3]});
そのほかの例
ブロックの中で使うなら、let の変数のスコープはそのブロックの中に制限されます。スコープが自身の宣言された関数全体になる var との違いに注意してください。
var a = 1;
var b = 2;
if (a === 1) {
var a = 11; // スコープはグローバル
let b = 22; // スコープは if ブロック内
console.log(a); // 11
console.log(b); // 22
}
console.log(a); // 11
console.log(b); // 2
しかし、下記の var と let 宣言の組み合わせは、var がブロックの先頭に配置されているため、SyntaxError です。 これによって、変数が暗黙的に再宣言されます。
let x = 1;
if (true) {
var x = 2; // 再宣言による SyntaxError
}
仕様
| 仕様 | ステータス | コメント |
|---|---|---|
| ECMAScript 2015 (6th Edition, ECMA-262) Let and Const Declarations の定義 |
標準 | 初期定義。let 式や let ブロックは定義されていない。 |
| ECMAScript Latest Draft (ECMA-262) Let and Const Declarations の定義 |
ドラフト |
ブラウザー互換性
| デスクトップ | モバイル | サーバー | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
let | Chrome
完全対応
49
| Edge
完全対応
14
| Firefox
完全対応
44
| IE
部分対応
11
| Opera 完全対応 17 | Safari 完全対応 10 | WebView Android
完全対応
49
| Chrome Android
完全対応
49
| Firefox Android
完全対応
44
| Opera Android 完全対応 18 | Safari iOS 完全対応 10 | Samsung Internet Android
完全対応
5.0
| nodejs 完全対応 6.0.0 |
凡例
- 完全対応
- 完全対応
- 部分対応
- 部分対応
- 実装ノートを参照してください。
- 実装ノートを参照してください。
- ユーザーが明示的にこの機能を有効にしなければなりません。
- ユーザーが明示的にこの機能を有効にしなければなりません。