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 的预编译过程!如果你有任何疑问或想法,欢迎随时交流

相关推荐
刘一说11 分钟前
Vue 组件不必要的重新渲染问题解析:为什么子组件总在“无故”刷新?
前端·javascript·vue.js
徐同保1 小时前
React useRef 完全指南:在异步回调中访问最新的 props/state引言
前端·javascript·react.js
刘一说2 小时前
Vue 导航守卫未生效问题解析:为什么路由守卫不执行或逻辑失效?
前端·javascript·vue.js
一周七喜h2 小时前
在Vue3和TypeScripts中使用pinia
前端·javascript·vue.js
weixin_395448912 小时前
main.c_cursor_0202
前端·网络·算法
东东5163 小时前
基于vue的电商购物网站vue +ssm
java·前端·javascript·vue.js·毕业设计·毕设
MediaTea3 小时前
<span class=“js_title_inner“>Python:实例对象</span>
开发语言·前端·javascript·python·ecmascript
梦梦代码精4 小时前
开源、免费、可商用:BuildingAI一站式体验报告
开发语言·前端·数据结构·人工智能·后端·开源·知识图谱
0思必得04 小时前
[Web自动化] Selenium执行JavaScript语句
前端·javascript·爬虫·python·selenium·自动化
程序员敲代码吗4 小时前
MDN全面接入Deno兼容性数据:现代Web开发的“一张图”方案
前端