震惊:v8引擎竟是如此操作代码(JS预编译)

JavaScript 的执行流程包含了编译阶段和执行阶段,理解这两者对于掌握 JavaScript 的工作原理至关重要。在本文中,我们将通过 V8 引擎如何执行代码的具体示例,来帮助你清晰地理解编译和执行的先后顺序,特别是如何进行预编译(Hoisting)操作。

1. V8 引擎的编译与执行流程

V8 引擎的工作分为两个主要阶段:

  • 编译阶段:V8 首先将 JavaScript 代码解析成抽象语法树(AST),并进行即时编译(JIT)转换为机器码。这一阶段主要是为执行代码做准备。
  • 执行阶段:在字节码或机器码生成后,V8 开始执行 JavaScript 代码。在执行过程中,涉及到变量和函数的赋值、调用等。

2. 作用域提升(Hoisting)

作用域提升 是指在编译阶段,JavaScript 会将变量声明(var)和函数声明提前到当前作用域的顶部。这意味着,在代码执行之前,JavaScript 已经知道了变量和函数的名字,但未初始化的变量会被赋值为 undefined,函数会直接指向函数体。

下面的代码展示了这一过程:

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

解释:

  • 执行上下文(Execution Context) :当 fn 被调用时,V8 引擎会为该函数创建一个执行上下文(EC)。在函数被编译时,a、b 和 c 等变量和函数会被提升到当前执行上下文的顶部。
  • AO(Activation Object) :fn 函数内的所有声明(如变量和函数)会被存储在 AO 对象中。图中的注释部分展示了函数体被预编译后的 AO 结构:
javascript 复制代码
AO = {
  a: undefined,  // 函数 a 被提升,但被初始化为 undefined
  b: undefined,  // 变量 b 被提升
  c: undefined   // 函数 c 被提升
}

代码行为分析:

  1. 当调用 fn(1) 时,a 作为形参被传入值 1。在函数体内,a 会被提升并初始化为 undefined。
  2. 第一个 console.log(a) 输出的是 undefined,因为变量 a 已经被提升,但没有被赋值。
  3. 之后,var a = 123; 会将 a 的值更新为 123,第二个 console.log(a) 输出 123。
  4. function a() 被提升并覆盖了 var a 的值,但由于函数声明在执行时发生作用,所以对 a 的值不会有影响。
  5. console.log(b) 输出 function() {},因为 b 被定义为一个匿名函数,并且函数声明会在编译时提升。
  6. console.log(c) 输出 123,这是因为 c 被定义为指向 a,而 a 在函数内部的值是 123。

3. 全局执行上下文与局部执行上下文

除了函数内部的执行上下文外,全局执行上下文也遵循相同的编译与执行规则。全局上下文创建时,V8 引擎会创建一个 全局对象 (如 window 或 global),并将全局变量和函数声明提升到该对象上。

例如:

js 复制代码
var a = 10;
function foo() {
  console.log(a);  // undefined
  var a = 20;
}
foo();
console.log(a);  // 10

解释:

  • 在全局作用域,var a 被提升,初始化为 undefined。
  • 在 foo 函数内,var a 同样会被提升,初始化为 undefined。因此,函数内的 console.log(a) 输出 undefined。
  • 最后,console.log(a) 输出的是全局作用域中的 a,其值为 10。

4. 总结

通过上述代码示例,我们可以看到 V8 引擎如何在编译阶段进行作用域提升,并通过执行上下文管理变量和函数的声明。理解这个过程,可以帮助我们更好地掌握 JavaScript 的执行顺序和作用域规则。

主要要点:

  • 编译与执行:V8 引擎首先编译代码,再执行字节码。
  • 作用域提升(Hoisting) :变量和函数声明会被提升,但赋值会按顺序执行。
  • 执行上下文(EC) :每个函数的执行都会创建一个执行上下文,并在编译阶段完成预编译(提升)。
  • 全局与局部作用域:全局作用域和局部作用域会有不同的提升规则,但基本遵循相同的原理。

通过掌握这些基础知识,我们可以更轻松地理解 JavaScript 代码的执行顺序,并避免一些潜在的陷阱,好的姐妹们,咱们下期再见。

相关推荐
子兮曰4 天前
jev-ultrafast 深度解析:7 秒订机票的浏览器 Agent 是如何炼成的
前端·后端·agent
子兮曰4 天前
Jev 爆发一周:7 秒 Agent 背后的 System One 生态与三场争议
前端·后端·ai编程
前端小万4 天前
写公众号赚了 3000 块后,我做了一款叫 "一键成稿" 的软件
前端·微信小程序
爱勇宝4 天前
ZCode 开源 24 小时:一份没有历史的账本,回答不了"有没有偷代码"
前端·后端·chatglm (智谱)
三十而立洋4 天前
Cookie 详解:从产生到安全,一次讲透
前端·javascript
卡布鲁4 天前
把一个 Vite + Vue3 应用塞进 qiankun (React + Umi3) 主站:十个坑的复盘
前端·javascript·react.js
李少兄4 天前
JavaScript 隐式全局变量解析
javascript
汉堡大王95274 天前
Jev:不是聊天机器人, 而是一个智能 if 语句
前端·人工智能·后端
梦想很大很大4 天前
从运行事实到回归证据:Workrun 的 Telemetry 与 Evaluation 实践
前端·人工智能·后端
计算机魔术师4 天前
Meta Muse agent 接入 Shopify 的 Shop Pay 实现代理式购物
前端