在 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 代码的执行过程,避免出现意外的错误。