在 JavaScript 里,变量提升(Hoisting)是一个重要的特性,它允许我们在变量声明之前就使用这些变量。下面来详细解释其底层原理以及 JavaScript 引擎所做的工作。
底层原理
JavaScript 代码的执行分为两个阶段:编译阶段和执行阶段。变量提升就发生在编译阶段。
编译阶段
在这个阶段,JavaScript 引擎会对代码进行扫描,做以下两件事:
- 函数声明提升:引擎会识别所有的函数声明,并将这些函数的定义存储在内存中。函数在整个作用域内都是可访问的,无论它在代码中的位置如何。
- 变量声明提升:引擎会识别所有的变量声明,在内存中为这些变量分配空间,但不会对其进行赋值操作。变量默认会被初始化为
undefined
。
执行阶段
在编译阶段完成后,JavaScript 引擎会按照代码的顺序逐行执行代码。当遇到变量赋值语句时,才会将具体的值赋给相应的变量。
JavaScript 引擎的具体操作
下面通过代码示例来详细说明 JavaScript 引擎在编译和执行阶段的操作:
console.log(a); // 输出: undefined
var a = 10;
console.log(a); // 输出: 10
编译阶段
JavaScript 引擎扫描代码,发现 var a
这样的变量声明,然后在当前作用域的内存中为变量 a
分配空间,并将其初始化为 undefined
。
执行阶段
- 执行
console.log(a)
时,由于a
已经在编译阶段被初始化为undefined
,所以输出undefined
。 - 执行
var a = 10
时,将值10
赋给变量a
。 - 再次执行
console.log(a)
时,输出10
。
函数声明与变量声明的优先级
函数声明的优先级高于变量声明。如果函数和变量使用了相同的名称,函数会覆盖变量的声明。
console.log(a); // 输出: [Function: a]
var a = 10;
function a() {}
console.log(a); // 输出: 10
编译阶段
- 引擎先识别到函数声明
function a() {}
,将函数a
存储在内存中。 - 接着识别到变量声明
var a
,但由于函数声明的优先级更高,这个变量声明不会覆盖函数a
。
执行阶段
- 执行
console.log(a)
时,输出函数a
。 - 执行
var a = 10
时,将值10
赋给变量a
,此时变量a
覆盖了函数a
。 - 再次执行
console.log(a)
时,输出10
。
总结
变量提升是 JavaScript 编译阶段的一个特性,它使得变量和函数的声明可以在实际定义之前被使用。理解变量提升的底层原理,有助于我们更好地理解 JavaScript 代码的执行过程,避免出现意外的错误。