本文大量参考阮一峰老师ES6手册。
先是let的使用注意事项
let只在所在的代码块以及更深的代码块内有效
通常就是花括号包起来的代码块,形成的作用域叫块级作用域。
if (true) {
let a = 1;
if (true) {
a = 2;
console.log(a); // 2
}
console.log(a); // 2
}
比较特别的for循环
for循环还有一个特别之处,就是设置循环变量的那部分是一个父作用域,而循环体内部是一个单独的子作用域。
所以,for (var i = 0; i < 10; i++)
跟for (let i = 0; i < 10; i++)
就有区别,下面栗子可以证明:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 10
var a = [];
for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};
}
a[6](); // 6
我们知道,函数只有被调用的时候才会去考虑作用域,而且js的作用域特点是词法作用域,所以:
第一段代码,打印10
是因为i
是全局变量,当调用a[6]
的时候i
就是10
,所以打印10
没毛病。
第二段代码,a[6]
的console.log(i)
会去找i
,i
既然是由let
声明,就被禁锢在for作用域内,而且for循环10次能产生10个作用域,所以i
在那个作用域的值是6
,于是打印6
。
不变量提升
这个特性的优点我认为是:符合人脑思维:先声明,后使用。没声明就使用的话,会报错,更醒目,程序员更容易纠错。就像下面这样:
// var 的情况
console.log(foo); // 输出undefined,不报错
var foo = 2;
// let 的情况
console.log(bar); // 报错ReferenceError
let bar = 2;
要我说,其实这个特性没什么大用。尽早声明变量应该是写代码的起码规范。
“暂时性死区”(temporal dead zone,简称 TDZ)
这个现象是老外提出来的,但我并没有看出它跟“不变量提升”有什么区别。死区的原因不就是“变量不提升”+“块级作用域”么?
不变量提升也好,暂时性死区也好,都是强迫程序员写出更规范的代码,就认为是严格模式就得了。
不允许重复声明
这个跟var确实有鲜明对比。
简单一句话:铜锣湾只能有一个浩南!(如果出了铜锣湾那就随便了)
if (true) {
let a = 1;
if (true) {
let a = 2;
console.log(a); // 2
}
console.log(a); // 1
}
上面代码如果把两个let
改成var
,那么会输出两个2
,就是因为var允许重复声明。
块级作用域
块级作用域的优点:
- 模块化编程。
- 不需要特意写一个自执行函数来制造一个局部作用域。
到底能不能在块级作用域声明函数?
ES5是不允许的,但是ES6又允许了。本着兼容原则,最好是别这么干,也就是不要在块级作用域声明函数。如果必须要在块代码里写函数,就用函数表达式。
到底啥时候用var,啥时候用let?
var和let各自的适用场合:
- 为了兼容低版本IE,当然用var。除此之外,优先用let。
- 有人说,es6中用let,babel中用var,原因是babel会写很多垫片代码实现对let的支持,从代码量来讲得不偿失。
以下是const的注意事项
const跟let特性一致的地方
- 块级作用域
- 不提升
- 不可重复声明(重复声明跟再次赋值是两码事)
- 即使在全局作用域声明,也不会是
window
对象的属性
跟let特性的区别
就一条,let虽然不能重复声明,但是可以无限制的赋值,但const只能初始化的时候赋值一次。
到底啥时候用let,啥时候用const?
一句话:const永远优先原则。
- 在全局环境,不应该设置变量,只应设置常量。可能你有顾虑是我真的想让一个量被修改,又真的想让它在全局环境,怎么办?这种情况下,可以用这种方式:
const globelConst = {a: 1, b: 2}; // a和b的值确实是可以改变的,原因见下文
- 在局部环境,也应该优先考虑使用const,除非它真的需要改变值,那么就用let。
小心赋值复合类型数据
对于简单类型的数据(数值、字符串、布尔值),const的值是绝对不会变的,因为变量指向保存在特定内存地址的数据。但对于复合类型的数据(主要是对象和数组),变量指向的是对应内存地址的指针,const只能保证这个指针是唯一的指针,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个数组或者对象声明为常量必须非常小心。
如果想要真正冻结一个数组或者对象,可以用Object.freeze方法。
const foo = Object.freeze({});
// 常规模式时,下面一行不起作用;
// 严格模式时,该行会报错
// 总之是不能用
foo.prop = 123;
但是这个方法只能浅冻结,如果想深冻结,就得写个方法:
var deepFreeze = (obj) => {
Object.freeze(obj);
Object.keys(obj).forEach( (key, i) => {
if ( typeof obj[key] === 'object' ) {
deepFreeze( obj[key] );
}
});
};
网友评论