# JS中let、var、和const的区别
let 和 const 都是ES6中的提出的新的定义变量方法,其中 let 可以看作是更完美的 var ,因为 let 具有块级作用域和暂时性锁区。
首先我们需要知道变量提升这个概念
var a = 123 // 全局变量
fun(); // 因为函数会被提升到作用域顶部进行声明,因此可以在这直接调用
console.log(a) // 123 // 输出全局变量
function fun() {
console.log(a) //undefined 输出的是下面定义的变量a声明提升,默认值为undefined
var a = 234 // 局部变量
console.log(a) // 从当前局部作用域开始寻找变量a 所以输出234
}
2
3
4
5
6
7
8
# let具有块级作用域
因为在ES6之前,使用var声明变量,只有函数局部作用域和全局作用域,因此在块级声明的变量相当于全局变量
所以会出现以下情形:
{
var a = 'a'
let b = 'b'
}
console.log(a) // a
console.log(b) // ReferenceError b is not defined
2
3
4
5
6
let声明的变量拥有块级作用域。
# let 防止循环变量过度共享
当我们使用var变量进行for循环时,会出现以下情况
for (var i = 0; i < 3; i++) {
setTimeout(() => { // 同步注册回调函数到 异步的任务队列
console.log(i) // 执行这一步时 ,同步代码for循环已执行完成
}, 0)
}
// 输出3个3
console.log(i) // 3
2
3
4
5
6
7
这是由于 for 循环本身及回调函数都共享唯一的全局变量 i ,也就是说 console.log(i) 输出的都指向同一个 i 即循环之后的变量 i 。
而使用 let 申明变量,则 for 循环每次执行都是一个全新的独立作用域,相互之间不会影响,同时JavaScript引擎会记住每一次 let 的值,因此下一次创建时,会自动在此数值上累加,同时注意这里 let i=0 ,只执行了一次,后面循环只会执行 i++ 的步骤,而不会再次重新初始化变量
for (let i = 0; i < 3; i++) {
setTimeout(() => {
console.log(i) // 0 1 2
}
}
2
3
4
5
同时,需要注意 for 循环设置循环变量的那部分是一个父作用域,而循环提内部是一个单独的作用域
for (let i = 0; i < 3; i++) {
let i = 123
console.log(i) // 123 123 123
}
2
3
4
# 暂时性锁区
let 所声明的变量一定要在变量之后使用,否则会报如下错误
console.log(a) // ReferenceError: a is not defined
let a = 123
// 下面可以安全使用
2
3
ES6 明确规定,如果区块中存在
let和const命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。
var a = 1; {
console.log(a); // ReferenceError: Cannot access 'a' before initialization
let a = 123;
}
2
3
4
注意: let 其实也是存在变量提升,只不过与 var 变量提升不同的是, var 变量提升主要是在函数作用域而不是块级作用域,同时 let 与 const 在变量提升的时候不会将其初始化,它是一个未初始化的状态(uninitialized state),而 var 将其初始化为undefined,未初始化就意味着你不能访问它,知道你在那个作用域运行 let 或者 const 声明语句。
总之,在代码块内,使用
let命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。
上边的表示如下:
// TDZ开始
a = 123
console.log(a) // ReferenceError: a is not defined
let a // TDZ 结束
console.log(a) // undefined
a = 456
console.log(a) // 456
2
3
4
5
6
7
这时,如果在 let 前面使用 typeof 检测变量类型就会返回错误,而不是 undefined
# 变量不允许重复声明
let a = 123
let a = 234 // SyntaxError: Identifier 'a' has already been declared
2
# let声明的全局变量不是全局对象的属性
这意味这你不能再通过 window.变量名 的方式进行变量的访问,他们只存在于一个不可见的块作用域中,这个块理论上是Web页面中运行的所有JS代码的外层块。

# let不能重定义变量
let 不允许在相同作用域下重复声明一个变量
let a = 123
let a = 234
// SyntaxError: Identifier 'a' has already been declared
2
3
# const命令
const 用于声明一个常量,一旦声明,就不能再改变这个常量的值,因此在声明时,就必须立即初始化
const a = 12
a=34
// TypeError: Assignment to constant variable.
const a
a=34
// SyntaxError: Missing initializer in const declaration
2
3
4
5
6
7
const 与 let 作用域相同,存在块级作用域,同时也存在暂时性锁区。
const 实质上是保证变量所指向的内存地址的数据不得改变,对于简单数据,值保存在变量所指向的内存地址,等同于常量;而对于对象和数组等复杂数据,变量指向的内存地址,保存的只是一个指向实际数据的指针。
更多相关文档见阮一峰大神的ECMAScript 6 入门 (opens new window)