JS在引擎中的过程
词法分析:分词生成token
一个个字幕扫描,与定义好的JavaScript关键字进行比较。token是不可分割的最小单元。
js
[
{ type: 'Const', value: 'const', start: 0, end: 5 },
{ type: 'Identifier', value: 'a', start: 6, end: 7 },
{ type: 'Assign', value: '=', start: 8, end: 9 },
{ type: 'Number', value: '1', start: 10, end: 11, raw: '1' },
{ type: 'Semicolon', value: ';', start: 11, end: 12 },
]
语法分析:通过Parser生成AST(规定结构的对象)

生成中间字节码
由解释器生成可运行的字节码。(为了在跨平台的基础上做到提升效率,保护代码安全)
热点代码编译成机器码
被即时编译器转换成机器码。只有热点代码会编译成机器码,为了平衡启动速度和执行效率。热点代码(例如多次调用的函数,多次循环体等),引擎通过性能计数器监控代码执行频率,当达到阈值时触发编译。
可以看到,代码中的热点代码会被进行编译成机器码,下一次执行就能直接执行机器码来提高效率。但是我们JS中是动态类型的语言,一个变量可以赋值给不同的类型。就行这次翻译的机器码限定类型为Object类型,下次执行到发现是Array类型,那么又要重新进行编译。
asm.js出现
asm.js是一个Javascript的严格子集,合理合法的asm.js代码一定是合理合法的JavaScript代码,但是反之就不成立。同WebAssembly一样,asm.js不是用来给各位用手一行一行撸 的代码,asm.js是一个编译目标。
C / C++ 编译成 JS 有两个最大的困难。
- C / C++ 是静态类型语言,而 JS 是动态类型语言。
- C / C++ 是手动内存管理,而 JS 依靠垃圾回收机制。
asm.js 就是为了解决这两个问题而设计的:它的变量一律都是静态类型,并且取消垃圾回收机制。 除了这两点,它与 JavaScript 并无差异,也就是说,asm.js 是 JavaScript 的一个严格的子集,只能使用后者的一部分语法。
asm.js强制静态类型,举个例子。
js
function asmJs() {
'use asm';
let myInt = 0 | 0;
let myDouble = +1.1;
}
WebAssembly
如果你对 JS 比较了解,可能知道还有一种叫做 WebAssembly 的技术,也能将 C / C++ 转成 JS 引擎可以运行的代码。那么它与 asm.js 有何区别呢?
回答是,两者的功能基本一致,就是转出来的代码不一样:asm.js 是文本,WebAssembly 是二进制字节码,因此运行速度更快、体积更小。从长远来看,WebAssembly 的前景更光明。
优点:
- 🚀 性能接近原生 (Near-Native Performance)
这是 Wasm 最广为人知的优点。通过预编译的二进制格式和高效的 JIT 编译到原生机器码,Wasm 在执行 CPU 密集型任务时远超传统 JavaScript。
- 应用场景:3D 游戏、视频/音频处理、科学计算、加密、物理模拟。
- 🌐 语言生态的极大扩展 (Language Ecosystem Expansion)
Wasm 允许开发者使用他们最擅长的语言来为 Web 编写高性能模块,而不仅仅局限于 JavaScript。
- 支持的语言:C, C++, Rust 是"一等公民",Go, C#, Swift, Python, Java 等也通过不同工具链得到了支持。
- 核心价值:这意味着可以将数十年积累下来的、经过实战检验的非 JS 库和应用移植到 Web 上。
缺点:
- 🌉 与 JavaScript 的交互开销 (JavaScript Interop Overhead)
这是目前 Wasm 最大的性能陷阱。频繁地在 JS 和 Wasm 之间来回调用函数或传递数据是非常昂贵的。
-
问题根源:数据需要在线性内存和 JS 对象之间来回复制和转换。
-
负面影响:如果使用不当(例如,用 Wasm 频繁操作 DOM),性能可能比纯 JS 实现还要差得多。
- 🕸️ 无法直接访问 Web API (No Direct Web API Access)
Wasm 被关在沙箱里,它不能像 JS 那样直接调用 document.getElementById()
或 fetch()
。所有这些操作都必须通过 JS 作为"中间人"来完成,这增加了复杂性和"胶水代码"的数量。