let 语句声明一个块级作用域的本地变量,并且可选的将其初始化为一个值。
描述
let 允许你声明一个作用域或被限制在块级中的变量、语句或者表达式。
与var不同的是,它声明的变量只能是全局或者整个函数块的。换句话,块级 == { }
为什么取‘let’这个名字。
1 Let is a mathematical statement that was adopted by early programming languages like Scheme and Basic.
2 Variables are considered low level entities not suitable for higher levels of abstraction,
3 thus the desire of many language designers to introduce similar but more powerful concepts like in Clojure, F#, Scala,
4 where let might mean a value, or a variable that can be assigned, but not changed,
5 which in turn lets the compiler catch more programming errors and optimize code better.
6 JavaScript has had var from the beginning, so they just needed another keyword,
7 and just borrowed from dozens of other languages that use let already as a traditional keyword as close to var as possible,
8 although in JavaScript let creates block scope local variable instead.
1 Let是一个数学声明,是采用于早期的编程语言如Scheme和Basic。
2 变量被认为是不适合更高层次抽象的低级实体,因此许多语言设计者希望引入类似但更强大的概念,
3 如在Clojure、f#、Scala,let可能意味着一个值,或者一个变量可以赋值,但不能被更改,
4 这反过来使编译器能够捕获更多的编程错误和优化代码更好。
5 javascript从一开始就有var,所以他们只是需要另一个关键字,并只是借用了其他数十种语言,
6 使用let已经作为一个传统的尽可能接近var的关键字,虽然在javascript 中 let只创建块范围局部变量而已。
作用域规则
let 声明的变量只是在其声明的块或者子块中可用,这一点,与var相似。二者的主要区别在于var声明的变量的作用域是整个封闭函数,而let声明的作用域是块。
1 function varTest() {
2 var x = 1;
3 if (true) {
4 var x = 2; // 同样的变量!
5 console.log(x); // 2
6 }
7 console.log(x); // 2
8 }
9
10 function letTest() {
11 let x = 1;
12 if (true) {
13 let x = 2; // 不同的变量
14 console.log(x); // 2
15 }
16 console.log(x); // 1
17 }
简化内部函数代码
当用到内部函数时候,let 会让你的代码更加简洁。
1 var list = document.getElementById('list');
2
3 for (let i = 1; i <= 5; i++) {
4 let item = document.createElement('li');
5 item.appendChild(document.createTextNode('Item ' + i));
6
7 item.onclick = function(ev) {
8 console.log('Item ' + i + ' is clicked.');
9 };
10 list.appendChild(item);
11 }
12
13 // to achieve the same effect with 'var'
14 // you have to create a different context
15 // using a closure to preserve the value
16 for (var i = 1; i <= 5; i++) {
17 var item = document.createElement('li');
18 item.appendChild(document.createTextNode('Item ' + i));
19
20 (function(i){
21 item.onclick = function(ev) {
22 console.log('Item ' + i + ' is clicked.');
23 };
24 })(i);
25 list.appendChild(item);
26 }
以上示例的工作原理是因为(匿名)内部函数的五个实例引用了变量i的五个不同实例。注意,如果将let 替换为 var, 则它将无法正常工作,因为所有内部函数都将返回相同的i,6是最终值。此外我们可以通过将创建新元素的代码移动到每个循环的作用域来保持循环更清晰。
在程序或者函数的顶层,let 并不会像var 一样在全局对象上创建一个属性,比如:
1 var x = "apple";
2 let y = "apple";
3 console.log(this.x); //'apple'
4 console.log(this.y); //undefined
模仿私有接口
在处理构造函数的时候,可以通过let 绑定来共享一个或者多个私有成员,而不使用闭包:
1 var Thing;
2 {
3 let privateScope = new WeakMap();
4 let counter = 0;
5
6 Thing = function(){
7 this.someProperty = 'foo';
8 privateScope.set(this, {
9 hidden: ++counter,
10 });
11 };
12
13 Thing.prototype.showPublic = function(){
14 return this.someProperty;
15 };
16
17 Thing.prototype.showPrivate = function(){
18 return privateScope.get(this).hidden;
19 };
20 }
21
22 console.log(typeof privateScope); //undefined
23
24 var thing = new Thing();
25 console.log(thing); //Thing { someProperty: 'foo' }
26 console.log(thing.showPublic()); //foo
27 console.log(thing.showPrivate()); //1
let 暂存死区的错误
在相同的函数或者块级作用域内重新声明同一个变量会引发SyntaxError
1 if (x) {
2 let foo;
3 let foo; // TypeError thrown.
4 }
所以在同一个块区,以let声明的变量,只能声明一次。
其他情况
当在块中使用时,let 将变量的作用域限制为该块。注意var 的作用域在它被声明的函数内区别。
1 var a = 1;
2 var b = 2;
3
4 if(a === 1){
5 var a = 11;
6 let b = 22;
7
8 console.log(a); // 11
9 console.log(b); // 22
10 }
11 console.log(a); // 11
12 console.log(b); // 2
总之,let 的作用域是自己最近的{}