JavaScript底层原理之——从声明提升到预编译

98 阅读2分钟

前言

JavaScript作为一门弱类型脚本语言,具有独特的变量声明提升和预编译机制。本文将深入探讨这些概念,为读者提供全面的了解,并通过代码示例进行演示,看完这篇文章你会对js这门语言有个新的认识。

1. 声明提升

在JavaScript中,使用var关键字声明的变量会存在声明提升的现象。声明提升是指在代码执行之前,JavaScript引擎会将变量声明移动到当前作用域的顶部。这使得我们在变量声明之前就能够使用这些变量。

console.log(a)  // undefined
var a = 1

// 实际的执行逻辑
var a = undefined
console.log(a)
a = 1

上述代码中,虽然a是在console.log(a)之后进行了变量声明,但由于声明提升的存在,程序仍能正常执行。

2. 函数声明提升

与变量不同,函数声明会整体提升到其所在作用域的顶部。这使得我们在函数声明之前调用函数是合法的。

sayHello(); // Hello

function sayHello() {
  console.log("Hello");
}

// 实际的执行逻辑
function sayHello() {
  console.log("Hello");
}
sayHello()

3. 函数体内的预编译

当预编译发生在函数体内时,会执行以下步骤:

  1. 创建AO对象(Action Object)。
  2. 找到形参和变量声明,并将其作为AO的属性名,赋值为undefined
  3. 统一形参和实参。
  4. 在函数体内找函数声明,将函数名作为AO对象的属性名,值赋予函数体。
function fn(a) {
    console.log(a);     // function () 
    var a = 123
    console.log(a);     //  123
    function a() {}
    console.log(a);     //  123
    var b = function() {}       // 函数表达式,也是一种变量声明
    console.log(b);     //      function()
    function d() {}
    var d = a   
    console.log(d);     //  123
}
fn(1)

// 编译过程
// AO = {
//     a: undefined    1     function ()   123,   
//     b: undefined   function (),
//     d: undefined    function () 
// }

4. 全局预编译

全局预编译与函数体内的预编译类似,但是发生在全局作用域中。步骤如下:

  1. 创建GO对象(Global Object)。
  2. 找到变量声明,并将变量名作为GO的属性名,赋值为undefined
  3. 在全局中找函数声明,将函数名作为GO对象的属性名,值赋予函数体。
var global = 100
function fn() {
    console.log(global);    // 100
}
fn()


// 编译过程

// GO = {
//     global: undefined  100,
//     fn: function ()
// }

结语

通过深入了解JavaScript中的声明提升和预编译,我们能更好地理解代码执行的前期准备工作。这些机制的了解有助于我们编写更具可读性和可维护性的代码,并能更轻松地排查潜在的问题。在实际编码中,善用声明提升和预编译机制将提高代码的执行效率,使开发更加高效。