このページはコミュニティーの尽力で英語から翻訳されました。MDN Web Docs コミュニティーについてもっと知り、仲間になるにはこちらから。

View in English Always switch to English

let

Baseline Widely available

This feature is well established and works across many devices and browser versions. It’s been available across browsers since ⁨2016年9月⁩.

let 宣言は、再代入可能な、ブロックスコープのローカル変数を宣言します。任意で値を代入して初期化できます。

試してみましょう

let x = 1;

if (x === 1) {
  let x = 2;

  console.log(x);
  // 予想される結果: 2
}

console.log(x);
// 予想される結果: 1

構文

js
let name1;
let name1 = value1;
let name1 = value1, name2 = value2;
let name1, name2 = value2;
let name1 = value1, name2, /* …, */ nameN = valueN;

引数

nameN

宣言する変数または複数の変数の名前です。それぞれは JavaScript の正規の識別子または構造分解パターンである必要があります。

valueN 省略可

変数の初期値です。任意の正規の式にすることができます。デフォルト値は undefined です。

解説

let で宣言された変数のスコープは、その let 宣言を直近の中括弧で囲む、次のいずれかの構文です。

上記のいずれもない場合は、次のようになります。

  • モジュールモードで実行中のコードで場合は、現在のモジュール
  • スクリプトモードで実行中のコードでは、グローバルスコープ

var と比較して、let 宣言には次のような違いがあります。

  • let 宣言は関数ではなく、ブロックのスコープになります。

  • let 宣言は、宣言の場所に到達した後にのみアクセスできます(一時的なデッドゾーンを参照)。このため、let 宣言は一般に巻き上げされないと考えられています。

  • let 宣言は、スクリプトの最上位で宣言された場合に globalThis のプロパティを作成しません。

  • let 宣言は、同じスコープ内では他のどの宣言においても再宣言できません。

  • let文ではなく宣言を始めます。それは、ブロックの本体として単独の let 宣言を使用することはできないということです(変数にアクセスする方法がないため、当然です)。

    js
    if (true) let a = 1; // SyntaxError: Lexical declaration cannot appear in a single-statement context
    

なお let は、厳格モードでない場合、var または function で宣言する際に識別子名として使用することができますが、予期しない構文場の曖昧さを避けるため、let を識別子名として使うのは避けてください。

多くのスタイルガイド(MDN を含む)では、変数がそのスコープ内で再代入されない場合、let よりも const を使用することを推奨しています。これにより、変数の型(場合によっては値)が不変であることが明確になります。変更可能な非プリミティブ型には let を推奨している場合もあります。

let キーワードに続くリストはバインドリストと呼ばれ、カンマで区切られます。ここでカンマはカンマ演算子ではなく、= 記号は代入演算子ではありません。後続の変数の初期化子は、リスト内の先行する変数を参照できます。

一時的なデッドゾーン (TDZ)

letconstclass で宣言された変数は、ブロックの始まりからコードが実行されて変数が宣言され初期化される行に到達するまでは、「一時的なデッドゾーン」(TDZ) 内にあると言います。

TDZ の中にいる間は、変数は値で初期化されておらず、アクセスしようとすると ReferenceError が発生します。変数は、宣言されたコードの行まで実行されると、値で初期化されます。変数宣言で初期値を指定しなかった場合は、undefined という値で初期化されます。

これは var で宣言された変数が宣言前にアクセスされた場合に undefined の値を返すのとは異なります。以下のコードは、letvar が宣言された行より前のコードでアクセスされた場合に、異なる結果が得られることを示しています。

js
{
  // TDZ はスコープの先頭で始まる
  console.log(bar); // "undefined"
  console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
  var bar = 1;
  let foo = 2; // TDZ の終了(foo について)
}

「一時的」という言葉が使われているのは、このゾーンがコードが書かれた場所(位置)ではなく、実行の順序(時間)に依存するゾーンであるためです。例えば、下記のコードは、let 変数を使用する関数は変数宣言の前に現れていますが、その関数が TDZ の外で呼び出されているため、動作します。

js
{
  // TDZ はスコープの先頭から始まる
  const func = () => console.log(letVar); // OK

  // TDZ の中では、letVar にアクセスすると `ReferenceError` が発生する

  let letVar = 3; // TDZ の終わり(letVar について)
  func(); // TDZ の外での呼び出し
}

typeof 演算子を使用して一時的なデッドゾーン内の変数の型を確認するしようとすると、ReferenceError が発生します。

js
{
  typeof i; // ReferenceError: Cannot access 'i' before initialization
  let i = 10;
}

これは、宣言されていない変数に対して typeof を使用した場合、undefined の値と見なされるのとは異なります。

js
console.log(typeof undeclaredVariable); // "undefined"

メモ: let および const 宣言は、現在のスクリプトが処理される際にのみ処理されます。1 つの HTML 内でスクリプトモードで実行される 2 つの <script> 要素がある場合、1 つ目のスクリプトは、2 つ目スクリプトで宣言された最上位の let または const 変数に対する TDZ 制限の対象外です。ただし、1 つ目のスクリプトで let または const 変数を宣言した場合、2 つ目スクリプトで再度宣言すると再宣言エラーが発生します。

再宣言

let 宣言は、(訳注:同じ名前の)他の宣言、例えば let, const, class, function, var, import 宣言と同じスコープに置くことはできません。

js
{
  let foo;
  let foo; // SyntaxError: Identifier 'foo' has already been declared
}

関数本体内の let 宣言は、引数と同じ名前を持つことはできません。catch ブロック内の let 宣言は、catch でバインドされた識別子と同じ名前を持つことはできません。

js
function foo(a) {
  let a = 1; // SyntaxError: Identifier 'a' has already been declared
}
try {
} catch (e) {
  let e; // SyntaxError: Identifier 'e' has already been declared
}

Firefox のウェブコンソール(ツール > ウェブ開発者ツール > ウェブコンソール)などの REPL で実験しているときに、同じ名前の let 宣言を 2 つの入力で実行すると、同じ再宣言エラーが得られる場合があります。この課題については、Firefox bug 1580891でさらなる議論をご覧ください。Chrome コンソールでは、異なる REPL 入力間で let を再宣言することができます。

switch 文には 1 つのブロックしかないため、エラーが発生してしまうかもしれません。

js
let x = 1;

switch (x) {
  case 0:
    let foo;
    break;
  case 1:
    let foo; // SyntaxError: Identifier 'foo' has already been declared
    break;
}

エラーを避けるためには、それぞれの case を新しいブロック文で囲んでください。

js
let x = 1;

switch (x) {
  case 0: {
    let foo;
    break;
  }
  case 1: {
    let foo;
    break;
  }
}

スコープのルール

let で定義された変数は、自身が定義されたブロックと、そこに含まれるサブブロックがスコープになります。この点において let のふるまいは var にとてもよく似ています。大きな違いは、var で定義された変数のスコープはそれを含んでいる関数全体になるということです。

js
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
}

プログラムや関数の最上位においては、letvar とは異なり、グローバルオブジェクト上にプロパティを生成しません。例を示します。

js
var x = "global";
let y = "global";
console.log(this.x); // "global"
console.log(this.y); // undefined

一時的なデッドゾーンと字句スコープと組み合わせた例

このブロックの foo は一時的なデッドゾーンの中にあります。

js
function test() {
  var foo = 33;
  if (foo) {
    let foo = foo + 55; // ReferenceError
  }
}
test();

外側の var foo に値があるため、if ブロックは実行されます。しかし、字句スコープにより、この値はブロックの内部では利用できません。if ブロックの内部にある識別子 foolet foo になります。式 foo + 55ReferenceError が発生します。これは let foo の初期化がまだ完了していない、つまり一時的なデッドゾーンにあるためです。

この現象は、以下のような状況で紛らわしくなることがあります。let n of n.a という命令は、すでに for...of ループのブロックのプライベートスコープ内にあります。そのため、識別子 n.a は、命令自体の最初の部分 (let n) にある 'n' オブジェクトのプロパティ a として解決されます。その宣言文にはまだ到達・完了していないため、まだ一時的なデッドゾーン内にあるとみなされます。

js
function go(n) {
  // n はここでは定義されていない
  console.log(n); // { a: [1, 2, 3] }

  for (let n of n.a) {
    //          ^ ReferenceError
    console.log(n);
  }
}

go({ a: [1, 2, 3] });

そのほかの場面

ブロックの中で使えば、let の変数のスコープはそのブロックの中に制限されます。スコープが自身の宣言された関数全体になる var との違いに注意してください。

js
var a = 1;
var b = 2;

{
  var a = 11; // スコープはグローバル
  let b = 22; // スコープは if ブロック内

  console.log(a); // 11
  console.log(b); // 22
}

console.log(a); // 11
console.log(b); // 2

しかし、下記の varlet 宣言の組み合わせは、var がブロックの先頭に配置されているため、SyntaxError になります。これによって、変数が暗黙的に再宣言されるからです。

js
let x = 1;

{
  var x = 2; // 再宣言のため SyntaxError
}

構造分解による宣言

それぞれの = の左側もバインドパターンである可能性があります。このことは、複数の変数を一度に作成することができるということです。

js
const result = /(a+)(b+)(c+)/.exec("aaabcc");
let [, a, b, c] = result;
console.log(a, b, c); // "aaa" "b" "cc"

詳しい情報は構造分解を参照してください。

仕様書

Specification
ECMAScript® 2026 Language Specification
# sec-let-and-const-declarations

ブラウザーの互換性

関連情報