随着Web网页的广泛发展,拥有JIT技术加持后,JavaScript的执行效率大幅提升,但由于解释型语言的特点,以及动态类型的天生缺陷,在某些CPU密集计算型的应用场景下,JavaScript的性能仍然力不从心。而且有大量C/C++等编译型语言的软件和库,也有着搬到Web上执行的需求,但如果全部用JavaScript语言重写,显然代价十分巨大。
一系列原因催生出了WebAssembly技术的诞生,先从asm.js说起。
一 asm.js
1.1 发展历史
2011年,在Mozilla工作的Alon Zakai,想把自己以前开发的C++ 游戏跑在浏览器里,但是又不想用JavaScript重写,就想能不能把C++ 编译为JavaScript呢?于是他创建了 Emscripten 项目,用LLVM将C++编译为字节码,再将字节码编译为JavaScript,成功地将C++游戏跑在了浏览器。
当时Mozilla的CTO正是JavaScript的作者Brendan Eich,他的梦想是让Web可以运行任何应用,而Alon的项目正在实现他的梦想,于是大力支持,Emscripten成为了公司的一个正式开源项目。
但仅仅将C++编译为JavaScript还不够,因为性能不够好。主要原因是JavaScript是动态类型语言,JS引擎在生成机器码时,必须动态判断变量类型,这带来了大量的冗余代码,必然存在性能损耗。
Mozilla的JS引擎工程师Luke Wagner告诉Alon,如果Emscripten能生成特定的隐含类型的JS代码,他就可以通过优化JS引擎来提升性能,例如:
function add(x, y) {
a = x | 0; // 参数x为整数
b = y | 0; // 参数y为整数
return a + b | 0; // add函数的返回值也是整数
}
通过在JS代码中增加 | 0,JS引擎就能推断出add函数的参数和返回值都是整数,从而直接编译为整数加法,提高了机器码的执行效率。对这种语法的进一步规范化,就产生了asm.js。
2013年,他们把100万行C++代码的虚幻游戏引擎编译为asm.js,在浏览器中流畅的运行了3D游戏《史诗城堡》。

1.2 原理
asm.js并不是一种新的语言,它只是一种遵守特定规范的JavaScript,本质上还是JavaScript。它可以手工编写,但可读性较差,所以通常是由 Emscripten工具来编译生成。
它有以下两个特点。
- 变量都是静态类型
- 没有垃圾回收机制
前面说过,普通JS代码的执行流程是:JS源码 -> 解析器 转成 抽象语法树 -> 解释器 转为 字节码 -> 编译器 转为优化后的机器码 -> 执行。
在这过程中,由于JS的动态类型特征,同一变量上一次执行还是Object,下一次就可能变成String了,这导致JIT编译过的机器码失去作用,只能重新进行编译。asm.js 约定静态类型就避免了这种情况的发生。
针对asm.js进行优化后的JS引擎,如果发现运行的是asm.js,便可以通过AOT静态编译的方式,将代码编译成机器码(且因为是静态类型,机器码可以得到极其优化)。当JS引擎再次执行(甚至是第一次执行)这段代码时,就可以直接使用前面编译过的机器码来执行了。
通过不断的优化,测试数据表明,asm.js的性能可以达到原生C/C++代码的50%。在Firefox浏览器率先支持asm.js后,其它的Chrome、Safari、Edge等浏览器相继优化其JS引擎来执行asm.js。
二 WebAssembly
2.1 发展历史
和普通JS代码相比,虽然asm.js性能提升很大,但它始终是个包含JS代码的文本,JS引擎还是需要去解析和编译,那为什么不在浏览器中增加一个虚拟机,从而能直接运行字节码呢?让越多的应用跑在Web上,并且性能越好,用户花在Web上的时间就越多,这符合各家浏览器厂商的共同利益。
因此,在2015年,各大浏览器厂商联手宣布开发WebAssembly。2017年,几大浏览器相继支持。2019年,W3C发布正式标准,WebAssembly成为继HTML、CSS、JavaScript之后的第4种Web语言。
现在C/C++、Rust、Java等越来越多的强类型编程语言,都可以编译为WebAssembly字节码,直接运行在浏览器中。就像Java字节码一样,一次编译到处运行,借助于浏览器,从而具有了跨平台特性。
2.2 原理
严格说,WebAssembly不是一种编程语言,而是一种可以在浏览器中直接运行的二进制格式,这种格式称为WASM。
测试数据表明,WebAssembly比asm.js平均快了33.7%,且文件体积平均小了62.5%,另外WebAssembly在编译速度、启动速度、内存使用等方面的表现也非常好。
WebAssembly并不是要取代JavaScript,只是把更多的编程语言和应用带到Web中,且具有接近于原生的性能,是JavaScript能力的补充,使Web更加丰富和强大。
三 总结
asm.js生成的是JavaScript代码,而WebAssembly生成的是WASM格式的二进制字节码,因此WebAssembly的体积更小,运行速度更快。
asm.js的兼容性比WebAssembly好,因为它本身就是JavaScript,所有浏览器都支持asm.js的运行,当然现在主流的浏览器也都支持WebAssembly,无需担心兼容问题。知名软件AutoCAD、Google Earth都借助于WebAssembly技术,移植到了浏览器中。
WebAssembly给Web带来了更多的可能性,对于CPU密集型的应用,比如音频、视频、直播、机器学习、AR、VR、游戏等,WebAssembly既可以帮助突破性能瓶颈,也可以让其他语言的代码库得到充分利用。因此,WebAssembly的应用还刚刚开始,发展潜力巨大。