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
, …,valueN
- 变量的初始值。可以是任意合法的表达式。
描述
let
允许你声明一个作用域被限制在 var
和 let
的不同之处在于后者是在编译时才初始化(见下面)。
就像window
对象的属性。
可以从这里了解我们为什么使用“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
一样,let
不会在全局对象里新建一个属性。比如:
位于函数或代码顶部的var
声明会给全局对象新增属性, 而let
不会。例如:
var x = 'global'; let y = 'global'; console.log(this.x); // "global" console.log(this.y); // undefined
模仿私有成员
在处理构造函数的时候,可以通过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 thrown. }
在 switch
语句中只有一个块,你可能因此而遇到错误。
let x = 1; switch(x) { case 0: let foo; break; case 1: let foo; // SyntaxError for redeclaration. break; }
然而,需要特别指出的是,一个嵌套在 case 子句中的块会创建一个新的块作用域的词法环境,就不会产生上诉重复声明的错误。
let x = 1; switch(x) { case 0: { let foo; break; } case 1: { let foo; break; } }
暂存死区
与通过 var
声明的有初始化值 undefined
的变量不同,通过 let
声明的变量直到它们的定义被执行时才初始化。在变量初始化前访问该变量会导致 ReferenceError
。该变量处在一个自块顶部到初始化处理的“暂存死区”中。
function do_something() { console.log(bar); // undefined console.log(foo); // ReferenceError var bar = 1; let foo = 2; }
暂存死区与 typeof
与通过var
声明的变量, 有初始化值 undefined
和只是未声明的变量不同的是,如果使用typeof
检测在暂存死区中的变量, 会抛出ReferenceError
异常:
// prints out 'undefined'
console.log(typeof undeclaredVariable);
// results in a 'ReferenceError'
console.log(typeof i);
let i = 10;
暂存死区和静态作用域/詞法作用域的相关例子
由于词法作用域,表达式(foo + 55)
内的标识符foo
被认为是if块的foo变量,而不是值为33的块外面的变量foo。
在同一行,这个if块中的foo
已经在词法环境中被创建了,但是还没有到达(或者终止)它的初始化(这是语句本身的一部分)。
这个if块里的foo
还依旧在暂存死区里。
function test(){ var foo = 33; { 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; // the scope is global let b = 22; // the scope is inside the if-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 }
规范
Specification | Status | Comment |
---|---|---|
ECMAScript 2015 (6th Edition, ECMA-262) Let and Const Declarations |
Standard | Initial definition. Does not specify let expressions or let blocks. |
ECMAScript Latest Draft (ECMA-262) Let and Const Declarations |
Draft |
浏览器兼容性
Desktop | Mobile | Server | |||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
let |
Chrome Full support 49
|
Edge Full support 14
|
Firefox Full support 44
|
IE Partial support 11
|
Opera Full support 17 | Safari Full support 10 | WebView Android Full support 49
|
Chrome Android Full support 49
|
Firefox Android Full support 44
|
Opera Android Full support 18 | Safari iOS Full support 10 | Samsung Internet Android Full support 5.0
|
nodejs Full support 6.0.0 |
Legend
- Full support
- Full support
- Partial support
- Partial support
- See implementation notes.
- See implementation notes.
- User must explicitly enable this feature.
- User must explicitly enable this feature.