let
let 宣告用於宣告可重新賦值的塊級作用域區域性變數,可以選擇將其初始化為一個值。
試一試
let x = 1;
if (x === 1) {
let x = 2;
console.log(x);
// Expected output: 2
}
console.log(x);
// Expected output: 1
語法
let name1;
let name1 = value1;
let name1 = value1, name2 = value2;
let name1, name2 = value2;
let name1 = value1, name2, /* …, */ nameN = valueN;
引數
描述
使用 let 宣告的變數的作用域是以下最接近包含 let 宣告的由大括號括起來的語法之一:
- 塊語句
switch語句try...catch語句- 如果
let在語句的頭部,則為其中一個for語句的語句體 - 函式體
- 靜態初始化塊
或者如果以上都不適用:
- 對於在模組模式下執行的程式碼,為當前模組
- 對於在指令碼模式下執行的程式碼,為全域性作用域。
與 var 相比,let 宣告具有以下區別:
-
let宣告的作用域是塊以及函式。 -
當在指令碼的頂層宣告時,
let宣告不會在globalThis上建立屬性。 -
let宣告不能在同一作用域內被任何其他宣告重新宣告。 -
let開啟的是_宣告_,而不是_語句_。這意味著你不能將獨立的let宣告用作塊的主體(這是有道理的,因為沒有辦法訪問該變數)。jsif (true) let a = 1; // SyntaxError: Lexical declaration cannot appear in a single-statement context
請注意,在非嚴格模式下,當使用 var 或 function 宣告時,let 允許作為識別符號名稱,但你應該避免使用 let 作為識別符號名稱,以防止出現意外的語法歧義。
許多程式碼風格指南(包括 MDN 的)建議,只要變數在其作用域內不被重新賦值,就使用 const 而不是 let。這使得意圖明確,即變數的型別(或原始值的值)永遠不會改變。其他人可能更喜歡對被修改的非原始值使用 let。
let 關鍵字後面的列表稱為_繫結列表_,並用逗號分隔,其中逗號_不是_逗號運算子,= 符號_不是_賦值運算子。列表中後面變數的初始化器可以引用列表中較早的變數。
暫時性死區 (TDZ)
使用 let、const 或 class 宣告的變數從塊的開始到程式碼執行到達變數宣告和初始化的地方,都被認為處於“暫時性死區”(TDZ)中。
在 TDZ 內部,變數尚未用值初始化,任何嘗試訪問它的行為都將導致 ReferenceError。當執行到達程式碼中宣告變數的位置時,變數將用值初始化。如果變數宣告時未指定初始值,它將用 undefined 值初始化。
這與 var 變數不同,var 變數如果在宣告之前訪問,將返回 undefined 值。下面的程式碼演示了在宣告位置之前訪問 let 和 var 時,程式碼中結果的不同。
{
// TDZ starts at beginning of scope
console.log(bar); // "undefined"
console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
var bar = 1;
let foo = 2; // End of TDZ (for foo)
}
使用“暫時性”一詞是因為該區域取決於執行順序(時間),而不是程式碼編寫順序(位置)。例如,下面的程式碼有效,因為儘管使用 let 變數的函數出現在變數宣告之前,但該函式是在 TDZ 之外_呼叫_的。
{
// TDZ starts at beginning of scope
const func = () => console.log(letVar); // OK
// Within the TDZ letVar access throws `ReferenceError`
let letVar = 3; // End of TDZ (for letVar)
func(); // Called outside TDZ!
}
對 TDZ 中的變數使用 typeof 運算子會丟擲 ReferenceError
{
typeof i; // ReferenceError: Cannot access 'i' before initialization
let i = 10;
}
這與對未宣告變數和值為 undefined 的變數使用 typeof 不同。
console.log(typeof undeclaredVariable); // "undefined"
注意: let 和 const 宣告僅在當前指令碼被處理時才會被處理。如果您在同一 HTML 中有兩個以指令碼模式執行的 <script> 元素,則第一個指令碼不受第二個指令碼中宣告的頂級 let 或 const 變數的 TDZ 限制,但如果您在第一個指令碼中聲明瞭一個 let 或 const 變數,在第二個指令碼中再次宣告它將導致重新宣告錯誤。
重新宣告
let 宣告不能與任何其他宣告在同一作用域內,包括 let、const、class、function、var 和 import 宣告。
{
let foo;
let foo; // SyntaxError: Identifier 'foo' has already been declared
}
函式體內的 let 宣告不能與引數同名。catch 塊內的 let 宣告不能與 catch 繫結的識別符號同名。
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
}
如果你在 REPL(例如 Firefox Web 控制檯(工具 > Web 開發者 > Web 控制檯))中進行實驗,並在兩個不同的輸入中執行兩個同名的 let 宣告,你可能會得到相同的重新宣告錯誤。有關此問題的進一步討論,請參閱 Firefox bug 1580891。Chrome 控制檯允許在不同的 REPL 輸入之間重新宣告 let。
你可能會在 switch 語句中遇到錯誤,因為只有一個塊。
let x = 1;
switch (x) {
case 0:
let foo;
break;
case 1:
let foo; // SyntaxError: Identifier 'foo' has already been declared
break;
}
為了避免錯誤,請將每個 case 包裝在一個新的塊語句中。
let x = 1;
switch (x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break;
}
}
示例
作用域規則
透過 let 宣告的變數在其宣告的塊以及任何包含的子塊中都具有作用域。透過這種方式,let 的工作方式與 var 非常相似。主要區別在於 var 變數的作用域是整個封閉函式。
function varTest() {
var x = 1;
{
var x = 2; // same variable!
console.log(x); // 2
}
console.log(x); // 2
}
function letTest() {
let x = 1;
{
let x = 2; // different variable
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
TDZ 與詞法作用域結合
以下程式碼在所示行會產生 ReferenceError:
function test() {
var foo = 33;
if (foo) {
let foo = foo + 55; // ReferenceError
}
}
test();
if 塊被評估,因為外部的 var foo 有一個值。然而,由於詞法作用域,這個值在塊內部不可用:if 塊_內部_的識別符號 foo 是 let foo。表示式 foo + 55 丟擲 ReferenceError,因為 let foo 的初始化尚未完成——它仍然在暫時性死區。
在以下情況中,這種現象可能會令人困惑。指令 let n of n.a 已經位於 for...of 迴圈塊的作用域內。因此,識別符號 n.a 被解析為指令自身第一部分(let n)中 n 物件的屬性 a。這仍然處於暫時性死區,因為其宣告語句尚未被執行和終止。
function go(n) {
// n here is defined!
console.log(n); // { a: [1, 2, 3] }
for (let n of n.a) {
// ^ ReferenceError
console.log(n);
}
}
go({ a: [1, 2, 3] });
其他情況
當在塊內部使用時,let 將變數的作用域限制在該塊中。請注意與 var 的區別,var 的作用域在其宣告的函式內部。
var a = 1;
var b = 2;
{
var a = 11; // the scope is global
let b = 22; // the scope is inside the block
console.log(a); // 11
console.log(b); // 22
}
console.log(a); // 11
console.log(b); // 2
然而,下面的 var 和 let 宣告的組合是一個 SyntaxError,因為 var 沒有塊級作用域,導致它們在同一個作用域內。這導致了變數的隱式重新宣告。
let x = 1;
{
var x = 2; // SyntaxError for re-declaration
}
使用解構進行宣告
每個 = 的左側也可以是一個繫結模式。這允許一次建立多個變數。
const result = /(a+)(b+)(c+)/.exec("aaabcc");
let [, a, b, c] = result;
console.log(a, b, c); // "aaa" "b" "cc"
欲瞭解更多資訊,請參閱解構。
規範
| 規範 |
|---|
| ECMAScript® 2026 語言規範 # sec-let-and-const-declarations |
瀏覽器相容性
載入中…
另見
varconst- 提升
- ES6 深入:
let和const,發表於 hacks.mozilla.org (2015) - Firefox 44 中
let和const的突破性變化,發表於 blog.mozilla.org (2015) - 你不知道的 JS:作用域與閉包,第三章:函式作用域 vs. 塊級作用域,作者 Kyle Simpson
- 什麼是暫時性死區?,發表於 Stack Overflow
- 使用
let和var有什麼區別?,發表於 Stack Overflow - JavaScript 中為什麼選擇 'let' 作為塊級作用域變數宣告的名稱?,發表於 Stack Overflow