--- 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
, …, varN
value1
, 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 const
let
and const
in Firefox 44let
and var
?