震惊: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)。在函数被编译时,abc 等变量和函数会被提升到当前执行上下文的顶部。
  • 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 引擎会创建一个 全局对象 (如 windowglobal),并将全局变量和函数声明提升到该对象上。

例如:

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 代码的执行顺序,并避免一些潜在的陷阱,好的姐妹们,咱们下期再见。

相关推荐
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了9 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅9 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅9 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅10 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊10 小时前
jwt介绍
前端
爱敲代码的小鱼10 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte10 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc