「这是我参与2022首次更文挑战的第2天,活动详情查看:2022首次更文挑战」
为什么 let 有变量提升?搞懂这个问题之前我们要知道什么是变量提升。
什么是变量提升(hoisting)
变量提升是 JavaScript 引擎在执行一段代码时做的预处理工作,预处理会把变量的声明放到函数或全局的顶部,这样能保证变量在使用的时候都声明过了。
console.log(a)
var a = 1
//上面代码经过变量提升后会处理成,注意这里是引擎内部的效果,不会修改代码
var a
console.log(a)
a = 1
let 声明的变量和 var 声明的变量的区别
var 声明变量
var a = 2
function foo() {
a = 1
return
var a
}
foo()
console.log(a) //2
上面例子中最后会输出 2,foo 中的 a = 1
并没有修改到函数外面的变量 a,说明这里的 a 是后面用 var a
声明的 a。
let 声明变量
var a = 2
function foo() {
try {
a = 1 //Uncaught ReferenceError: Cannot access 'a' before initialization
return
let a
} catch(e){}
}
foo()
console.log(a) //2
上面例子中最后会输出 2,foo 中的 a = 1
抛错了,说明这里的 a 是后面用 let a
声明的 a。
所以 let 声明的变量也有提升,只是表现的方式和 var 不一样,如果在声明前使用会抛错。
let 变量提升带来的暂时性死区
function foo() {
console.log(a)
let a = 1
}
//预处理后
function foo() {
let a
//---------------变量a的暂时性死区开始---------------
console.log(a)
//---------------变量a的暂时性死区结束---------------
a = 1
}
与通过 var 声明的有初始化值 undefined 的变量不同,通过 let 声明的变量直到它们的定义被执行时才初始化。在变量初始化前访问该变量会导致 ReferenceError。该变量处在一个自块顶部到初始化处理的“暂存死区”中。
除了变量提升还有哪些提升?
除了变量会提升外,函数和类在预处理时也会提升。
Function 提升
foo('varlet')
function foo(name) {
console.log(`Hello ${name}.`) //Hello varlet.
}
Function 通过提升后,可以在任意位置调用。
Class 提升
var foo = new Foo('varlet')
foo.sayHello()
class Foo {
constructor(name) {
this.name = name
}
sayHello() {
console.log(`Hello ${this.name}.`) //Hello varlet.
}
}
Class 通过提升后不能在声明前使用,因为类没有使用默认值进行初始化,所以如果在声明前使用了类会抛 ReferenceError 错误。
变量提升带来的方便和不便
变量提升能让我们更方便得写代码,譬如调用函数时不必担心函数是否已经声明。但是也带来了坏处,譬如经典的循环陷阱
function foo() {
var arr = new Array(10)
for (var i = 0; i < 10; i++) {
arr[i] = function() {
console.log(i)
}
}
return arr
}
var arr = foo()
arr[0]() //10
arr[1]() //10
arr[2]() //10
在 ES6 中我们可以使用 let 代替 for 循环中的 var 来声明变量,把 i 的作用域变成块级作用域,从而避免循环陷阱问题。
如有错误欢迎指出,欢迎一起讨论!