likes
comments
collection
share

探析js引擎的预编译机制

作者站长头像
站长
· 阅读数 14

引言

声明提升指的是变量和函数的声明会被提升到当前作用域的顶部,但变量的初始化并不会被提升,看以下两个简单例子:

变量的声明提升——

console.log(a);  // 打印 undefined
var a = 10;

// 相当于
// var a
// console.log(a);
// a = 10

函数声明会整体提升——

test(); // 成功调用,打印出20
function test() {
    var a = 20;
    console.log(a);   
}

// 相当于
// function test() {
//     var a = 20;
//     console.log(a); 
// }
// test();

于是,在大多数的书籍中,你都能看见这两句话:

  1. 变量声明,声明提升
  2. 函数声明,整体提升

但事实上碰到面试官出题时,只记这两句话往往不能帮你快准狠地得出正确答案,所以接下来我们就来探析js的预编译机制,真正理解声明提升背后的原理

前置知识

  • AO:活动对象(Activation Object)。当一个函数��调用时,JS引擎会创建一个新的执行上下文,并在其中构建活动对象,其中包含了函数的参数、局部变量、函数声明
  • GO:全局对象(Global Object)。存放所有的全局变量和函数,以及内置的对象和方法。在浏览器环境下,全局对象通常是 window 对象;而在 Node.js 环境中,全局对象则是 global 对象

预编译

代码在执行前需要进行编译操作,用于确定代码之间的各种关联 。编译总是发生在函数执行的前一刻。

  • 全局预编译三部曲:
  1. 创建GO对象
  2. 找变量声明,将变量名作为GO的属性名,值为undefined
  3. 在全局找函数声明,将函数名作为GO的属性名,值为该函数体
  • 函数体内预编译四部曲:
  1. 创建一个AO对象
  2. 找形参和变量声明,将形参和变量名作为AO的属性名,值为undefined
  3. 形参和实参统一(因为对象里面的key是不可能重复的)
  4. 在函数体内找函数声明,将函数名作为AO的属性名,值为该函数体

举两个栗子

var a = 10;
function fn(a) {
    var a = 2;
    function a() {}
    console.log(a); 
}
fn(3)

  • 首先创建GO对象,其中包含变量声明a值为undefined、函数体fn
  • 执行到第七行代码,调用函数fn时,创建一个AO对象
  • 找到形参a和声明的变量a,统一为a=3
  • 找到函数声明a,将a重新赋值,a=[Function: a]
  • 预编译完成,开始执行代码
  • 执行到第三行将a重新赋值2,第四行不是可执行代码,所以最后打印出来a为2

第二个例子同理,快来自己试试看吧:

function fn(a) {
    console.log(a);  
    var a = 123
    console.log(a);  
    function a() {}
    console.log(a); 
    var b = function() {}  // 函数表达式
    console.log(b);  
    function c() {}  // 函数声明
    var c = a
    console.log(c);  // 123
}
fn(1)

依次打印出:[Function: a] 123 123 [Function: b] 123

认真读完后相信你已经对js预编译有了更加深刻的理解,阅读过程中若发现有任何不足或亮点,欢迎大家在评论区指出交流,我们共同进步~

转载自:https://juejin.cn/post/7398352986324664346
评论
请登录