let const 与 var

109 阅读4分钟

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