最近在读《你不知道的 JavaScript(上卷)》,做了一些学习笔记,分享出来。

提升

变量声明的提升在我平时的工作中比较少,而函数声明的提升则用得比较多。变量声明的提升在平时的开发中是比较容易被忽略的。

思考如下代码:

1
2
3
4
5
a = 2

var a

console.log(a)

我刚刚看到的时候以为会输出 undefined,但是其实输出的是 2。

变量声明和函数声明从它们在代码中出现的位置被“移动”到了其所在作用域的最上面,这个过程就叫做提升(Hoisting)。

编译器

JavaScript 引擎在执行代码之前,会先进行编译,而提升的这个过程,就是在编译这个过程中完成的。

上面的代码经过编译器处理之后,会变成如下:

1
2
3
4
5
var a

a = 2

console.log(a)

只提升声明,不提升赋值或其他逻辑

只有声明会被提升,赋值或者其他运行逻辑会留在原地,不会被提升。

1
2
3
console.log(a) // undefined

var a = 2

这段代码的 var a = 2 声明了 a 变量并且赋值为 2,但是却打印出了 undefined;这是因为变量 a 的声明被提升了,而赋值没有提升,相当于如下代码:

1
2
3
4
5
var a

console.log(a) // undefined

a = 2

函数作用域中的提升

在函数的作用域内也会有提升:

1
2
3
4
5
6
7
function foo() {
a = 2
var a
console.log(a) // 2
}

foo()

let 和 const 声明没有提升

只有用 var 声明的变量会被提升,用 let 和 const 声明的变量都无效:

1
2
3
console.log(a) // 报错!

let a = 2

提升的优先级

函数声明的提升会比变量声明的提升优先级更高:

1
2
3
4
5
6
7
8
9
10
11
foo() // 1

var foo

function foo() {
console.log(1)
}

foo = function() {
console.log(2)
}

后声明的函数会覆盖先声明的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
foo() // 3

var foo

function foo() {
console.log(1)
}

foo = function() {
console.log(2)
}

function foo() {
console.log(3)
}

遵循编译器的原则

由于编译器会把函数和变量的声明提升到函数和变量当前作用域的顶部,为了避免出错,我们平时开发的时候就应该养成先声明变量,再使用变量的习惯。