ES6 的一个重要特性就是有了块的概念,引入了let、const声明变量,在刚开始接触这两个标识符的时候,就当做var来用的,但是他们之间其实是有很多差别的。本篇整理了一下,简单的地方只是列了一个提纲,文末还有他们之间的区别,当用到的时候可以回来看。
js中对于未声明的变量会报语法错误,对于未初始化值的变量会报undefined。
1、var声明及变量提升机制
作用域:函数作用域、全局作用域
变量提升:变量的==声明位置==提升到当前作用域顶部,但是变量的==初始化位置==不变。
2、块级声明
块级声明 ----->块级标识符----->let、const
----->块级作用域(亦称词法作用域):指函数内部、{}之间的区域
块级变量只在代码块内有效,代码块外立即销毁。
2.1 let声明
2.2 禁止重命名
2.3 const声明
要求声明时必须进行初始化,且值一旦被设定后不可更改。
但是对于js对象而言, const声明不允许修改绑定,但是允许修改绑定的值
2.4 临时死区
ES6 明确规定,如果区块中存在let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
总之,在代码块内,使用let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
临时死区内,使用该变量,如:typeof,都会报语法错误; 如果没有形成临时死区,使用该变量,typeof,则不会报错。 例:2.4.2、2.4.3
2.4.1
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
2.4.2
typeof x; // ReferenceError
let x;
上面代码中,变量x使用let命令声明,所以在声明之前,都属于x的“死区”,只要用到该变量就会报错。因此,typeof运行时就会抛出一个ReferenceError。
作为比较,如果一个变量根本没有被声明,使用typeof反而不会报错。
2.4.3
typeof undeclared_variable // "undefined"
上面代码中,undeclared_variable是一个不存在的变量名,结果返回“undefined”。所以,在没有let之前,typeof运算符是百分之百安全的,永远不会报错。现在这一点不成立了。这样的设计是为了让大家养成良好的编程习惯,变量一定要在声明之后使用,否则就报错。
有些“死区”比较隐蔽,不太容易发现。
function bar(x = y, y = 2) {
return [x, y];
}
bar(); // 报错
上面代码中,调用bar函数之所以报错(某些实现可能不报错),是因为参数x默认值等于另一个参数y,而此时y还没有声明,属于“死区”。如果y的默认值是x,就不会报错,因为此时x已经声明了。
function bar(x = 2, y = x) {
return [x, y];
}
bar(); // [2, 2]
另外,下面的代码也会报错,与var的行为不同。
// 不报错
var x = x;
// 报错
let x = x;
// ReferenceError: x is not defined
上面代码报错,也是因为暂时性死区。使用let声明变量时,只要变量在还没有声明完成前使用,就会报错。上面这行就属于这个情况,在变量x的声明语句还没有执行完成前,就去取x的值,导致报错”x 未定义“。
2.5 循环中的块作用域绑定
var:for与 for in、 for of let:for与 for in、 for of const:for与 for in、 for of
2.6 全局作用域绑定
3、块级绑定最佳实践
默认使用const,只在确实需要改变变量的值时才使用let。
4、块级标识符与var的区别:
1、变量提升:var声明的变量会变量提升,而块级标识符不会。
2、变量重命名:var允许同一个作用域下变量重名,但是会覆盖,而块级标识符不允许在同一个作用域下变量重名。
3、全局作用域绑定: var声明的变量,如果和全局对象相同,则仍然是覆盖原则。 而块级标识符和全局对象没有任何关系。
全局对象:浏览器环境中是window,node环境中是global。
var a = 2;
var a = 3; // a=3