ES6(一):let和const
一、let
1. let基本用法
相当于var,但是又与var不同,因为let声明的变量只能在let所在的代码块中有效。
从以下两段代码以及对应的输出结果可以很明显的看出var与let的区别。
code:
for (var i = 0; i < 5; i++) {
console.log(i);
}
console.log(i);
result:
0
1
2
3
4
5
code:
for (let i = 0; i < 5; i++) {
console.log(i);
}
console.log(i);
result:
0
1
2
3
4
ReferenceError: i is not defined
2. 块级作用域
众所周知,在ES5中只有全局作用域和函数作用域,没有我们所谓的块级作用域。
这不禁让我想起一个面试题:
code:
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(new Date, i);
}, 1000);
}
console.log(new Date, i);
问:上面这段代码输出的结果是什么?
仔细琢磨一下我们很容易得出正确的答案:
result:
2018-10-31T06:55:30.020Z 5
2018-10-31T06:55:31.021Z 5
2018-10-31T06:55:31.021Z 5
2018-10-31T06:55:31.022Z 5
2018-10-31T06:55:31.022Z 5
2018-10-31T06:55:31.022Z 5
仔细看一下发现:由于setTimeout会被JavaScript延迟执行,因此是先输出最底部的console.log,隔一秒之后再执行循环里面的consol.log,此时i已经全部是5。
那我们怎么样才能使输出结果变成想要的0,1,2,3,4呢?
明白了let的原理之后其实问题变得很简单:
code:
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(new Date, i);
}, 1000);
}
console.log(new Date, i);
这样就行了吗?
result:
ReferenceError: i is not defined
哈哈,刚刚还说过let只在所在的代码块里面有效,这里最外层的console.log很明显找不到i,所以报出引用错误。想要得到正确答案其实还需要去掉最后一行console.log。
当然这个题目要搁以前的话,首先想到的解决方案应该是IIFE(Immediately Invoked Function Expression:声明即执行的函数表达式)来解决闭包造成的问题。
code:
for (var i = 0; i < 5; i++) {
(function(j) {
setTimeout(function() {
console.log(new Date, j);
}, 1000);
})(i);
}
console.log(new Date, i);
result:
2018-10-31T07:17:45.825Z 5
2018-10-31T07:17:46.827Z 0
2018-10-31T07:17:46.827Z 1
2018-10-31T07:17:46.827Z 2
2018-10-31T07:17:46.827Z 3
2018-10-31T07:17:46.827Z 4
由此我们是不是就可以得出一个结论,有了let,我们就可以抛弃IIFE了?
这个暂时还不好说,我们还是继续看看let的其他特性。
3. 没有变量提升
var命令会产生变量提升的现象,这使得js这门语言变得并不严谨,这一点是特点也是鸡肋。用var声明的变量,如果我们在声明之前使用,则它的值为undefined。
而当我们用let定义变量时,就必须严格按照先定义再使用的原则了,反之则会抛出一个大大的引用错误(ReferenceError),显然这更符合人们的使用习惯。
code:
console.log(a);
var a = 2;
console.log(b);
let b = 2;
result:
undefined
ReferenceError: b is not defined
4. 暂时性死区及不能重复声明
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
“暂时性死区”也意味着typeof不再是一个百分之百安全的操作。
let不允许在相同作用域内,重复声明同一个变量。
二、const
1. const基本用法
const命令是声明一个常量,用法和let一样。
- 和let的相同之处是:
- const与let作用域相同,都是只在声明的代码块中起作用
- const也不会提升所声明的常量
- const也不能重复声明
- 不同之处是const声明的常量在声明时就必须赋值,因为一旦声明就不能改变改常量的值
2. 真的不能改变吗?
const实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,const只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。
code&result:
const a=1;
a=2;//TypeError
const obj={
b:3,
c:4
}
obj.b=5;
console.log(obj);//{b:5,c:4}
参考资料
- 《ECMAScript 6 入门》——阮一峰
- 破解前端面试(80% 应聘者不及格系列):从闭包说起——王仕军知乎专栏:前端周刊
- ECMAScript2015官方标准