Skip to content

JavaScript 引擎揭秘:从代码到内存的旅程

第0章:为什么要学习引擎层?

“知其然,更要知其所以然。”
—— 当你开始问“变量存在哪?”,你就不再是普通开发者。

你是否也曾被这些问题困扰?

1. 写闭包时,总觉得“变量被记住了”,但不知道“记在哪”

js
function createCounter() {
  let count = 0;
  return function() {
    count++;
    console.log(count);
  };
}
const counter = createCounter();
counter(); // 1
counter(); // 2
  • 你知道 count 没有被销毁。
  • 但你知道它存在内存的哪个角落吗?
  • 是存在 counter 函数里?还是某个神秘的“环境”中?
  • 为什么 createCounter 都执行完了,count 还活着?

如果你只能回答“因为闭包”,那你只看到了魔法的表象。

2. 看 Uncaught TypeError 的堆栈信息,却看不懂“at”后面的调用链

text
Uncaught TypeError: Cannot read property 'name' of undefined
    at logName (user.js:5)
    at processUser (app.js:12)
    at handleClick (ui.js:8)
    at HTMLButtonElement.onclick (index.html:1)
  • 你知道错误发生在 user.js 第5行。
  • 但你知道这个“调用栈”是怎么形成的吗?
  • 为什么是 logName → processUser → handleClick 这个顺序?
  • at” 后面的 HTMLButtonElement.onclick 是什么意思?

如果你只会“看行号”,你就错过了定位复杂问题的关键线索。

3. 性能优化时,只知道“减少闭包”,但不知道“为什么闭包慢”

你可能听过:

“闭包会影响性能,尽量少用。”

  • 你照做了,删掉了很多闭包。
  • 但你知道为什么闭包会影响性能吗?
  • 是因为作用域链变长了?
  • 是因为变量存在堆上,GC 压力大?
  • 是因为内联缓存失效?

如果你只是“听别人说”,你就无法在必须用闭包时做出最优决策

4. 面试被问“执行上下文是什么”,只能背定义,无法深入

面试官问:

“什么是执行上下文?”

你回答:

“执行上下文是 JS 执行代码的环境,包含 this、变量环境和词法环境。”

  • 背得很标准。
  • 但面试官追问:
    • “那 this 存在哪?”
    • “变量环境和词法环境是对象吗?”
    • “它们在内存中是怎么组织的?”

你卡住了。

因为你背的是“逻辑概念”,而不是“物理实现”。

学习引擎层的价值:你将超越谁?

你能做到你将超越
看懂 V8 垃圾回收日志只会 const / let 的人
诊断闭包内存泄漏背“闭包是内部函数访问外部变量”的人
理解 this 绑定的本质记忆“谁调用谁就是 this”的人
优化作用域链查找不知道“TDZ”和“内联缓存”的人

真正的高手,不只写代码,更懂代码如何被执行

想象两位医生:

  • 医生A:背熟了所有病症的治疗方案,但不知道人体解剖结构。
  • 医生B:理解器官如何工作,能根据症状反推病因。

谁更能应对复杂病例?

开发者也一样

  • 大多数人是“症状治疗型”:遇到内存泄漏,就 fn = null
  • 少数人是“系统诊断型”:能看内存快照,定位是哪个 Context 没被释放。

本专栏的目标

我们不满足于:

let 是块级作用域,var 是函数级。”

我们要深入:

let 的变量存储在堆上的 Context 对象中,通过 [[Environment]] 指针被闭包引用,GC 只有在所有引用断开后才会回收它。”

我们将一起探索:

  • 栈帧如何承载 this
  • Context 对象如何存储变量
  • 作用域链如何被 [[Environment]] 指针物理连接
  • 闭包如何“绑架”外层变量的生命

准备好了吗?

从下一章开始,我们将:

  1. 拆开“调用栈”,看看每个栈帧里装了什么
  2. 潜入“堆内存”,找到变量真正的归宿
  3. 追踪“指针”,看清作用域链的物理路径

你不再只是“写 JavaScript”,
你将开始“操控 JavaScript 的执行世界”。