前言
由于ES6的出现,变量声明不再单调,除了可以用var
外,还可以使用let
和const
。
- ES5:
var
- ES6:
let
、const
下面来了解下它们声明的变量有哪些区别。
1. 变量的挂载不同
- var 声明的变量会挂载在 window 上,而 let 和 const 声明的变量不会
- let 、const 声明的变量会处于当前作用域中
<script>
var a = 100;
console.log(window.a); // 100
let b = 100;
console.log(window.b); // undefined
const c = 100;
console.log(window.c); // undefined
console.log(b); // 100 - 当前作用域
涉及到作用域有关知识
2.有无变量提升
- var 声明变量存在变量提升
- let 和 const 不存在变量提升
console.log(a);
var a = 100; // undefined =》变量提升,已声明未赋值,默认undefined
console.log(b);
let b = 100; // Uncaught ReferenceError: Cannot access 'b' before initialization =》 未声明使用,报错
console.log(c);
let c = 100; // Uncaught ReferenceError: Cannot access 'b' before initialization =》 未声明使用,报错
可以同时关注下【函数提升】有关概念
3.能否重复声明
- 同一作用域下 var 可以声明同名变量
- 同一作用域下 let和const不能声明同名变量
var a = 100;
console.log(a); // 100
var a = 10;
console.log(a); // 10
let b = 100;
let b = 10; // Uncaught SyntaxError: Identifier 'b' has already been declared
if (true) {
let b = 10;
console.log(b); // 10 => 不同作用域内声明可以
}
虽然 var 可以声明同名变量,但是一般不会这么使用。变量名尽可能是唯一的。可关注下【JS变量命名规范】有关。
4.是否有块级作用域
- ES5 是没有块级作用域概念的,所以var声明自然没有
- let 和 const 声明形成块级作用域
if (true) {
var a = 100;
let b = 10;
const c = 10;
}
console.log(a); // 100
console.log(b); // Uncaught ReferenceError: b is not defined
console.log(c); // Uncaught ReferenceError: c is not defined
可关注 ES5 是如何模拟块级作用域的
5. 暂时性死区
let/const 存在暂时性死区,var 没有。下面新开标题详解。
6.const声明注意事项
- 一旦声明必须赋值,不能用 null 占位
- 声明一个常量,声明后不能再修改
- 如果声明的是复合类型数据,可以修改其属性
const a = 100;
// a = 200; // Uncaught TypeError: Assignment to constant variable
const list = [];
list[0] = 10;
console.log(list); // [10]
const obj = {a:100};
obj.name = 'apple';
obj.a = 10000;
console.log(obj); // {a:10000,name:'apple'}
暂时性死区
只要块级作用域内存在let命令,它所声明的变量就“绑定”(binding)这个区域,不再受外部的影响。
如果在声明变量或常量之前使用它, 会引发 ReferenceError
, 这在语法上成为 暂存性死区
(temporal dead zone,简称 TDZ)。
由于let、const没有变量提升,才产生了暂时性死区
if (true) {
// TDZ开始
tmp = 'abc'; // ReferenceError
console.log(tmp); // ReferenceError
let tmp; // TDZ结束
console.log(tmp); // undefined
tmp = 123;
console.log(tmp); // 123
}
上面代码中,在let命令声明变量tmp之前,都属于变量tmp的“死区”。
typeof不再安全
在暂时性死区内,typeof
不再是一个百分之百安全的操作
typeof x; // Uncaught ReferenceError: Cannot access 'y' before initialization =》报错:未声明不可用
let x;
typeof undefined_variable // undefined =》未声明的变量不会报错
隐蔽型死区
- 与词法作用域结合的暂存死区
function test() {
var foo = 100;
if (true) {
let foo = (foo + 100); // Uncaught ReferenceError: Cannot access 'foo' before initialization
}
}
test();
在 if 语句中,foo 使用 let 进行了声明,此时在 (foo + 100) 中使用的 foo 是 if 语句中的 foo,而不是外面的 var foo = 100;
由于赋值运算符是将右边的值赋予左边,所以先执行了 (foo + 100), 所以 foo 是在还没声明完使用,于是抛出错误。
function team(n) {
console.log(n);
for (let n of n.member) { // Uncaught ReferenceError: Cannot access 'n' before initialization
console.log(n)
}
}
team({member: ['tony', 'lucy']})
在 for 语句中,n 已经进入了块级作用域,n.member 指向的是 let n ,跟上一例子一样,此时 n 还未声明完,处于暂存死区,故报错。
- switch case中case语句的作用域
switch (x) {
case 0:
let foo;
break;
case 1:
let foo; // TypeError for redeclaration.
break;
}
会报错是因为switch中只存在一个块级作用域, 改成以下形式可以避免:
let x = 1;
switch(x) {
case 0: {
let foo;
break;
}
case 1: {
let foo;
break;
}
}
总结
暂时性死区是一个新概念,我们应该保持良好变量声明习惯,尽量避免触发。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。