--- title: let slug: Web/JavaScript/Reference/Statements/let tags: - ECMAScript 2015 - JavaScript - Language feature - Statement - Variable declaration - Variables - let - 変数 - 変数宣言 - 文 - 言語機能 translation_of: Web/JavaScript/Reference/Statements/let ---
let 文はブロックスコープのローカル変数を宣言します。任意で値を代入して初期化できます。
let var1 [= value1] [, var2 [= value2]] [, ..., varN [= valueN];
var1, var2, …, varNvalue1, value2, …, valueN {{optional_inline}}分割代入構文は、変数の宣言にも使用できます。
let { bar } = foo; // where foo = { bar:10, baz:12 };
/* これは、値が 10 の 'bar' という名前の変数を作成します。*/
let を使用することで、それが使用された{{jsxref("statements/block", "ブロック", "", 1)}}、文または式にスコープを限定した変数を宣言することができます。これは {{jsxref("statements/var", "var")}} キーワードのように、変数をブロックスコープに関係なく、グローバルや関数全体のローカルに定義するようなことはありません。他にも、{{jsxref("statements/var", "var")}} と let は、後者はパーサーが評価したときのみ値の初期化が行われる点が異なります。(下記参照)
{{jsxref("statements/const", "const", "Description")}} と同様に、let はグローバル (一番上のスコープ) で宣言されたときに {{domxref("window")}} オブジェクトのプロパティを生成しません。
なぜ "let" という名前が選ばれたのかについては、こちら で解説されています。
let で定義された変数は、自身が定義されたブロックと、そこに含まれるサブブロックがスコープになります。この点において let のふるまいは var にとてもよく似ています。大きな違いは、var で定義された変数のスコープはそれを含んでいる関数全体になるということです。
function varTest() {
var x = 1;
{
var x = 2; // 同じ変数です!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
{
let x = 2; // 異なる変数
console.log(x); // 2
}
console.log(x); // 1
}
プログラムや関数の最上位においては、let は var とは異なり、グローバルオブジェクト上にプロパティを生成しません。
var x = 'global'; let y = 'global'; console.log(this.x); // "global" console.log(this.y); // undefined
{{Glossary("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)が必要です。
同じ関数やブロックのスコープ内で同じ変数を再宣言すると {{jsxref("SyntaxError")}} が発生します。
if (x) {
let foo;
let foo; // SyntaxError が発生します。
}
{{jsxref("Statements/switch", "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;
}
}
{{jsxref("Statements/var", "var", "var_hoisting")}} で宣言された変数が undefined の値で始まるのとは異なり、let の変数は定義が評価されるまで初期化されません。変数を宣言より前で参照すると {{jsxref("ReferenceError")}} が発生します。変数はブロックの先頭から初期化が行われるまで、「一時的なデッドゾーン」にあるのです。
function do_something() {
console.log(bar); // undefined
console.log(foo); // ReferenceError
var bar = 1;
let foo = 2;
}
typeof単純に宣言されていない変数や undefined の値を持つ変数とは異なり、typeof 演算子を使用して一時的なデッドゾーン内の変数の型を確認するしようとすると、{{jsxref("ReferenceError")}} が発生します。
// 'undefined' を表示 console.log(typeof undeclaredVariable); // 'ReferenceError' が発生します console.log(typeof i); let i = 10;
字句スコープのため、式 (foo + 55) の中にある識別子 foo は if ブロックの foo と評価され、その上にある変数 foo (33 の値を持つ) とは評価されません。
同じ行では、if ブロックの foo が字句環境よりすでに生成されていますが、初期化に達していない (完了していない) 状態です (その分自身の一部であるため)。
このブロックの foo は一時的なデッドゾーンの中にあります。
function test(){
var foo = 33;
if(foo) {
let foo = (foo + 55); // ReferenceError
}
}
test();
この現象は、以下のような状況で混乱を催すかもしれません。let n of n.a という命令は、すでに for ループブロックの私的スコープの中になります。そのため、識別子 n.a は命令自身 (let n) の最初の部分にある 'n' オブジェクトのプロパティ 'a' として解決されます。
その宣言文にはまだ到達・完了していないため、まだ一時的なデッドゾーン内にあるとみなされます。
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 がブロックの先頭に配置されているため、{{jsxref("SyntaxError")}} になります。これによって、変数が暗黙的に再宣言されるからです。
let x = 1;
{
var x = 2; // 再宣言のため SyntaxError
}
| 仕様 |
|---|
| {{SpecName('ESDraft', '#sec-let-and-const-declarations', 'Let and Const Declarations')}} |
{{Compat("javascript.statements.let")}}
let and constlet and const in Firefox 44let and var?