var const let
在 ES6 之前,只有通过 var 关键字来声明变量,在 ES6 引入了 let 和 const
var
存在变量提升,即变量可以在声明之前使用,值为 undefined,var 声明的变量没有块级作用域,只有函数作用域
const
- 不存在变量提升,存在块级作用域,用来声明常量,且常量不可修改,必须初始化
- 我们常用 const 来声明一个常量,但是只限制于变量的引用,如果我们用 const 去定义一个对象,修改对象内部的属性并不会报错
js
const obj = {}
obj.name = 'zhangsan'
// 可以
obj = {
name: 'zhangsan'
}
// 报错 因为改变了变量的引用
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
let
- 不存在变量提升,存在块级作用域,不允许在相同作用域内重复声明同一个变量
- 在 let/const 声明变量之前的执行瞬间被称为“暂时性死区”(temporal dead zone),在此阶段引用后面才声明的变量都会抛出 ReferenceError
接下来我们看一段代码,分别用 var/let/const 声明一个变量,并且在声明之前去打印出来,看看结果
js
console.log(a) // undefined
console.log(b) // 报错
console.log(c) // 报错
var a = 1
let b = 2
const c = 3
1
2
3
4
5
6
2
3
4
5
6
var 声明的全局变量会被挂载到全局 window 对象上,而 let 和 const 不会
js
var a = 1
let b = 2
const c = 3
console.log(window.a) // 1
console.log(window.b) // undefined
console.log(window.c) // undefined
1
2
3
4
5
6
2
3
4
5
6
再来看一个经典面试题,先不要往下划,先想想打印的结果是什么
js
for (var i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i)
}, i * 1000)
}
1
2
3
4
5
2
3
4
5
答案揭晓:打印的结果是每隔一秒打印一次 3 这是因为在退出循环时,迭代变量保存的是导致循环退出的值,在之后执行超时逻辑时,所有的 i 都是同一个变量,所以输出的都是同一个值
这时我们稍微改动一下代码 把 var 换成 let
js
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i)
}, i * 1000)
}
1
2
3
4
5
2
3
4
5
此时打印的结果就是每隔一秒打印 0 1 2 let 声明迭代变量时 JS 会为每个迭代循环声明一个新的迭代变量,每个 setTimeout 引用的都是不同的变量实例,所以打印出来的是我们期望的值 而由于变量提升,var 声明的迭代变量会泄漏到循环外部 严格来讲,let 在 JavaScript 运行时中也会被提升,但由于“暂时性死区”(temporal dead zone)的缘故,实际上不能在声明之前使用 let 变量 这种每次迭代声明一个独立变量实例的行为适用于所有风格的 for 循环,包括 for-in 和 for-of 循环