1、变量提升(hoisting)
1 | console.log(a) // undefined |
上述代码会输出 undefined。
是由于var声明的变量会将声明提升到作用域的顶部(实际上let和const都会存在变量提升),也就等价于:1
2
3var a
console.log(a)
a = 1
这就是所谓的变量提升。不仅仅变量会被提升,函数也会被提升:1
2
3console.log(a) // function a() {}
var a = 1
function a() {}
2、临时性死区
在let和const关键字之前,JavaScript没有块级作用域,只有函数作用域。例如在for循环中使用var声明的变量i,在其之外也可以访问。1
2
3
4for(var i = 0; i<10; i++){
console.log(i);
}
console.log(i) // 10
使用let和const声明的关键字拥有块级作用域。
若将上述循环变量使用let声明,则在循环块外就无法访问了。那么这就产生了另一个问题:1
2
3
4for(var i = 0; i<10; i++){
console.log(a) // 报错
let a = 1;
}
在循环块中使用let声明了a变量,在声明之前使用会报错。这并不是说明使用let声明的变量不会进行提升,实际上a也会被提升到作用域顶端。只是从a被复制到作用域顶端这段范围被称为变量a的临时性死区,在这段区域内是无法访问变量a的。
变量提升并不是一个缺陷,而是为了解决一些问题而存在的,例如:1
2
3
4
5
6function a() {
b()
}
function b() {
a()
}
类似这种我中有你你中有我的情况,就可以利用变量提升很好的解决。
3、const不可变
const和let一样,也具有变量提升和临时性死区的特点。他们两唯一不同的是const声明的变量不允许改变(常量)。1
2const a = 10;
a = 1 // 报错
如果const定义了一个引用数据类型:1
2const a = {name: "hello"};
a.name = "world";
这样的改变是允许的,因为此时a是存在于栈区的一个名字,由于const的原因无法改变a对应栈区的内容,但是他所指向的对象是存放在堆区的,所以改变a.name是被允许的。
4、挂载对象
var和let/const还有一个明显的区别,就是var声明的变量是挂载到window下,可以通过window来访问,而let/const不行。1
2
3
4
5var a = 1
let b = 1
const c = 1
console.log(window.a) // 1
console.log(window.b, window.c) // undefined

