JavaScript预编译机制深度解析:从V8引擎到执行上下文

一、JavaScript执行流程全景图

JavaScript代码的执行流程可以概括为以下关键步骤:

  1. 读取代码:V8引擎加载JS代码
  2. 预编译阶段:创建执行上下文,进行变量/函数声明提升
  3. 执行阶段:逐行执行代码,进行赋值和函数调用

二、函数预编译四步曲

当函数被调用时,V8引擎会创建函数执行上下文,其预编译过程包含四个关键步骤:

  1. 创建函数执行上下文对象(AO)

    javascript 复制代码
    function example(a, b) {
        var c = 10;
        function d() {}
        var e = function() {};
    }
    example(1,2)
    // 预编译开始:创建函数的执行上下文对象
    example = {}
  2. 寻找形参和变量声明

    • 将形参和变量名作为执行上下文对象的属性名

    • 值为undefined

    javascript 复制代码
    example = {
        a: undefined, // 形参
        b: undefined, // 形参
        c: undefined, // 变量声明
        e: undefined  // 变量声明
    }
  3. 实参与形参值统一

    javascript 复制代码
    example(1, 2); // 调用时实参赋值
    example = {
        a: 1,       // 形参绑定实参值
        b: 2,       // 形参绑定实参值
        c: undefined,
        e: undefined
    }
  4. 找函数声明

    • 函数声明会覆盖同名的变量

    • 在函数体里找函数声明,函数名作为上下文对象的属性名

    • 值为函数体

    javascript 复制代码
    AO = {
        a: 1,
        b: 2,
        c: undefined,
        d: function d() {}, // 函数声明直接赋值
        e: undefined
    }

三、全局预编译三阶段

全局作用域的预编译过程略有不同:

  1. 创建全局上下文对象(GO)

    csharp 复制代码
    // 全局代码
    var a = 'test';
    function fn() {}
    
    // 创建GO对象
    GO = {}
  2. 找变量声明

    • 变量名作为 全局上下文对象的属性名
    • 值为undefined
    css 复制代码
    GO = {
        a: undefined  // 变量声明提升
    }
  3. 找函数声明

    • 函数名作为上下文对象属性名
    • 值为函数体
    javascript 复制代码
    GO = {
        a: undefined,
        a: function fn() {} // 函数声明提升
    }

四、预编译实战解析

看了那么多,来上手试试,下面代码的结果是什么?

1

css 复制代码
   var a=1
   function fn(){
   var a=2
   function a(){}
   var b=a
   console.log(a);
}
 fn()

预编译过程:

第一步,先创建全局上下文执行对象 放入调用栈 在GO中 。第二步查找变量声明 a 值设为undefined。第三步, 然后查找函数声明,函数名fn作为属性名,值为函数体function(){},最后进行代码执行,a=1 并且进入函数体中创建函数的执行上下文对象,查找形参和变量名 ,a值设为undefined ,b值设为undefined,,因为没有形参,所以不需要实参形参值统一 直接在函数体fn中查找函数声明 ,这里需要注意的是变量名和函数名一样的就直接覆盖,不会再创建一个变量,因为是在对象中key是唯一的 ,找到a的函数声明,a值改为函数体,预编译结束,执行代码 执行第3行a值改为2 第4行函数声明不调用不执行,第5行执行b值改为2,第6行输出a,a值为2

结果:

2

js 复制代码
function fn(a) {
  console.log(a);
  var a = 123;
  console.log(a);
  function a() {}
  var b = function () {};
  console.log(b);
  function d() {}
  var d = a;
  console.log(d);
}
fn(1);

第一步,先创建全局上下文执行对象 这里没有变量声明,直接代码执行。第二步,在函数体中创建函数的执行上下文对象,查找形参和变量名 ,形参a值设为undefined,变量a重复 ,b值设为undefinedd值设为undefined。第三步,然后形参和实参值统一 a=1。第四步,在函数体fn中查找函数声明 ,这里需要注意的是var b=fucntion(){}不是函数声明哦,聪明的小伙伴肯定发现了,这是函数表达式,相当赋值要等到执行阶段执行 ,找到d的函数声明,d值改为函数体,预编译结束,执行代码 执行第2行 打印空函数体 执行第3行a值改为123 第4行打印a的结果为123,第5行跳过,第6行输出b值赋为函数体,第7行打印b为空的函数体,第8行跳过,第9行d值改为123,第10行打印d值为123 结果:

3

js 复制代码
function fnn(a, b) {
  console.log(a);
  c = 0;
  var c;
  a = 3;
  b = 2;
  console.log(b);
  function b() {}
  console.log(b);
}

fn(1);

第一步先创建全局上下文执行对象 这里没有变量声明,直接代码执行。第二步在函数体中创建函数的执行上下文对象,查找形参和变量名 ,形参a值设为undefined变量a重复 ,形参b值设为undefinedc值设为undefined,然后形参和实参值统一 a=1,b值没有。第三步在函数体fn中查找函数声明 ,这里需要注意的是var b=fucntion(){}不是函数声明哦,聪明的小伙伴肯定发现了,这是函数表达式,相当赋值要等到执行阶段执行 ,找到b的函数声明,b值改为函数体,预编译结束,执行代码 执行第2行 ,打印结果1, 执行第3行c值改为0 第4行跳过,第5行a值设为3,第6行输出b值赋2,第7行打印b为2,第8行跳过,第9行打印b值为2 结果:

五、关键概念对比表

特性 变量声明 函数声明
提升机制 仅声明提升(值为undefined) 整个函数体提升
执行上下文创建时机 预编译阶段 预编译阶段
重复声明处理 后续声明被忽略 覆盖同名变量
块级作用域影响 受let/const约束 不受块级作用域影响

六、ES6带来的改变

  1. 暂时性死区(TDZ)

    javascript

    ini 复制代码
    console.log(a); // ReferenceError
    let a = 10;
  2. 块级作用域

    javascript

    javascript 复制代码
    {
        let b = 1;
        function fn() {}
    }
    console.log(b); // ReferenceError
    console.log(fn); // 函数可访问(非严格模式)

七、总结

JavaScript的预编译机制是其执行模型的核心:

  1. 全局预编译:创建GO → 变量声明 → 函数声明
  2. 函数预编译:创建AO → 形参/变量 → 实参绑定 → 函数声明
  3. ES6引入的let/const通过块级作用域和TDZ解决了变量提升问题
  4. 理解执行上下文生命周期是掌握JavaScript异步编程、闭包等高级特性的基础

希望本文能帮助你更好地理解 JavaScript 的预编译过程!如果你有任何疑问或想法,欢迎随时交流

相关推荐
zwjapple44 分钟前
docker-compose一键部署全栈项目。springboot后端,react前端
前端·spring boot·docker
像风一样自由20203 小时前
HTML与JavaScript:构建动态交互式Web页面的基石
前端·javascript·html
aiprtem4 小时前
基于Flutter的web登录设计
前端·flutter
浪裡遊4 小时前
React Hooks全面解析:从基础到高级的实用指南
开发语言·前端·javascript·react.js·node.js·ecmascript·php
why技术4 小时前
Stack Overflow,轰然倒下!
前端·人工智能·后端
GISer_Jing4 小时前
0704-0706上海,又聚上了
前端·新浪微博
止观止4 小时前
深入探索 pnpm:高效磁盘利用与灵活的包管理解决方案
前端·pnpm·前端工程化·包管理器
whale fall4 小时前
npm install安装的node_modules是什么
前端·npm·node.js
烛阴4 小时前
简单入门Python装饰器
前端·python
袁煦丞5 小时前
数据库设计神器DrawDB:cpolar内网穿透实验室第595个成功挑战
前端·程序员·远程工作