1. var的声明与变量提升
-
var声明的变量会有变量提升:不管你在哪个位置声明变量,解析代码时声明都被将提升到函数或全局作用域的顶部。 - 没有暂时性死区,在声明之前访问是
undefined,因为提升的是声明,还没有被初始化
2. 块级作用域(词法作用域)
- 块级作用域就是在当前代码块内定义的变量只能再当前代码块内被访问,会产生块级作用域的情况在函数里、在用
{}包括的代码块里
let:
-
let声明的变量作用域会被限制在当前代码块中,不存在变量提升。 - 在同一作用域代码块内,不能重复声明一个已有的标示符(已经被声明的变量),不然会报错
- 不会绑定全局作用域
window.let声明的变量是访问不到的
var name = "kimi";
// 语法错误
let name = "emoji";
var name = "kimi";
// 不会抛出错误
if(condition){
let name = "emoji";
}
const(常量):
- 声明后不能再被修改,所以需要声明的时候就进行初始化,不然会报语法错误
- 和
let一样不能重复声明 -
const会阻止对于变量绑定与变量自身值的修改,但是不会阻止对变量成员的修改
const声明一个对象是可以修改对象的成员变量的
const person = {
name: "kimi"
};
// 可以被正常修改
person.name = "emoji";
// 会报错
person = {
name: "emoji"
}
- 循环中的块级作用域与
var不同let、const都能在每次迭代时创建一个新的绑定,在循环体内创建的函数可以使用当前迭代所绑定的循环变量值,而不像var,统一使用循环结束时的变量值。
让一次性的循环计数器仅能在循环内部使用
for(let i = 1; i <= 5; i++){
setTimeout(function timer() {
console.log(i);
}, i*1000);
}
// 1,2,3,4,5
为什么使用let会输出不同值:
规范中实现var和let的底层处理方式不同,在for(let i = 1; i <= 5; i++)里会建立一个隐藏的作用域,然后每次迭代循环的时候都创建一个新的变量,使用之前迭代中同名变量的值将这个新创建的变量初始化,所以在执行的时候,根据块级作用域就可以正确输出当前值
循环中对i的调用不是同一个变量,所以不会和var一样,变量值是始终都是同一个
用伪代码来说明:
(let i = 1){
setTimeout(function timer() {
console.log(i);
}, i*1000);
}
(let i = 2){
setTimeout(function timer() {
console.log(i);
}, i*1000);
}
...
循环中for语句里使用const会报错,因为const声明的常量值不能被修改
总结:
let和const
-
let和const的唯一区别是:const声明并初始化后就不能被修改,let可以重复修改 - 不存在变量提升
- 不能被重复声明
块级作用域:
- 会有暂时性死区(
temporal dead zone,TDZ):当前的作用域内,使用let、const声明的变量在声明之前访问都会报错 - 暂时性死区是
JS社区用来描述let\const声明的变量为什么在声明处之前无法访问的一个描述,ECMAScript规范中没有被明确命名。