JavaScript 二进制的 AST

JavaScript 二进制的 AST

在这个博客文章中,我想介绍一下 JavaScript 二进制 AST,咱们但愿在咱们的项目中这将有助于使网页加载更快,以及其余一些好处。javascript

背景介绍

多年来,JavaScript 已经从最慢的脚本语言之一,从老爷车发展为兰博基尼,无论是经过 Web 浏览器仍是其余环境。它都可以快到能够运行桌面、服务器、移动甚至嵌入式应用程序。前端

随着 JavaScript 的增加,应用程序的复杂程度和规模都愈来愈复杂。然而,二十年前,少数使用过 JavaScript 的网站也就加载几千字节的 JavaScript,许多网站和非 Web 应用程序如今须要在用户开始实际使用以前加载几兆的 JavaScript 代码。java

“几兆的 JavaScript 代码”听起来会很陌生,可是像 Steam 这样的本地应用程序只有 3.1 兆(纯二进制,没有资源,没有调试符号,没有动态依赖,在个人 Mac 上测量的结果)。Telegram 是 11 兆,Opera 更新程序 是 5.8 兆。由于浏览器其实是动态依赖构建的,因此我并没算上 Web 浏览器的体积,但我估计Firefox 和 Chrome 有 100 余兆的大小。react

固然,大型 JavaScript 源代码有几个成本,包括:android

  • 重型网络传输;
  • 慢速启动。

咱们如今已经能在很短的时间内解析 JavaScript 代码,在之前一个大型的 web 应用例如 Facebook 在一台较好的电脑上经过 500ms-800ms 的时间编译完成。几乎没有理由相信随着时间的推移,JavaScript 应用程序会变得愈来愈小。ios

所以,Mozilla 和 Facebook 的一个联合小组决定开始研究一种新的机制,咱们相信经过二进制 AST 执行 JavaScript 能够极大地提升应用程序的速度。git

二进制 AST 简介

JavaScript 二进制 AST 的思想很简单:咱们能够经过发送二进制而不是发送文本源。github

让我来澄清一下:二进制 AST 源码至关于文本的源码。并不是一个新的语言,也不是 JavaScript 的子集或超集,它 JavaScript。它不是一个字节码,而是源代码的二进制表示形式。若是您愿意,这个二进制 AST 就是一种专为JavaScript而设计的,并为了解析速度而优化过的源代码。咱们还在构建一个能够提供可读的格式良好的源代码解码器。目前,这种形式并无保留注释,可是有一个保留注释的提议。web

生成一个二进制 AST 文件须要一个构建过程,咱们但愿这个过程越快越好。像 WebPack 或者 Babel 这样的构建工具会产生一个二进制的 AST 文件,所以,切换到二进制 AST 就像向构建传递一个标志同样简单,许多开发者已经开始使用。正则表达式

我想在我将来博客的文章中详细介绍一些二进制 AST 的标准和咱们的现状,如今,我来简述一下,早期的实验暗示咱们能够能获得很好的源压缩和可观的解析速度。

咱们已经研究二进制 AST 几个月了,如今项目已经做为 Stage 1 Proposal 被 ECMA TC-39 所接受。这是鼓舞人心的,可是仍是须要必定的时间,你才能看到全部的 JavaScript 虚拟机和工具链的实现。

对比一下

和压缩格式对比

大部分的 web 服务器在发送 JavaScript 的时候已经使用了例如 gzip 或者 brotli 这样的压缩工具将 JavaScript 压缩了。这大大减小了等待数据的时间。咱们在这里作的是一种专为 JavaScript 设计的格式。的确,在早期的原型内部使用 gzip,相比许多其余的技巧,咱们早期的原型有两个主要优点:

  • 它使得解析速度更快;
  • 根据早期的实验,咱们大幅度击败了 gzip 或 brotli。请注意,咱们的主要目的是使分析速度更快,所以在将来,若是咱们须要在文件大小和解析速度中作选择,咱们最有可能选择更快的解析。另外,使用的压缩格式的内部可能会改变。

和压缩工具相比

web 开发者早期使用的用来减小 JS 文件大小的传统工具,例如 UglifyJS 和 Google’s Closure Compiler,这些工具称为压缩工具。

压缩工具一般移除未使用的空格和注释、修改变量而后缩短名称,并使用一些其余转换来使程序更短。

虽然这些工具确实有用,但它们有两个主要缺点:

  • 它们并不试图更快地进行解析 —— 事实上,咱们已经目击了在不少状况下,缩小意外使得解析更慢;
  • 它们有使 JavaScript 代码更难阅读的反作用,包括重命名不便于阅读的变量和函数,使用奇怪的特征将声明的变量打包,等等。

相反,使用二进制 AST 转换:

  • 用于使解析更快;
  • 以易于解码的方式保留了源代码并容易阅读全部变量名等。

固然,若是不但愿保持源代码可读性的应用程序,混淆和二进制 AST 转换能够结合在一块儿。

和 WebAssembly 相对比

另外一个使人兴奋的旨在提高肯定的性能的 web 技术是 WebAssembly(wasm)。wasm 是为了使本地的应用被编译为一种格式,这种格式既能够有效地传输,也能够快速的解析,并经过 JavaScript 虚拟机以本地速度执行。

然而,设计者的意图是将 wasm 受限于本地代码,因此若是不是本地代码,JavaScript 将不起做用。

我不认为全部的 JavaScript 项目均可以经过 wasm 的编译。虽然这是可行的,但这将会是一项至关冒险的项目,由于这至少和开发一个新的 JavaScript 虚拟机的复杂度是相同的。同时还要确保仍然能够和 JavaScript 兼容(这是一个很是棘手的语言,而且每一年至少生成一次说明文档或扩展),固然,若是生成的代码比今天的 JavaScript 虚拟机慢的话,这个任务就没用了,JavaScript 虚拟机如今愈来愈快了。而且若是编译以后的代码运行速度过慢或者文件太大,会使得启动很是慢(这也是咱们在这里要解决的问题)或者使用编译的 JavaScript 库和(适用于浏览器应用程序的)DOM 致使没法工做。

如今,对这方面的探索绝对是一个有趣的工做,因此若是有人想证实咱们错了,不管如何,请这样作:)

提升缓存

当 JavaScript 代码被浏览器下载时,它被存储在浏览器的缓存中,以免之后再下载它。Chromium 和 Firefox 近期更新了他们的浏览器使得不只 JavaScript 源文件能够缓存,字节码也能够加入缓存。所以,能够很好地解决页面再次加载的解析时间问题。我不知道 Safari 和 Edge 在这方面的进展,因此他们可能也会有相似的技术。

恭喜 Chromium 和 Firefox,这些技术都很棒!事实上,他们很好地提升重载页面的性能。这对于那些自从上次访问 JavaScript 代码可是没有更新的页面很是有效。

咱们试图用二进制 AST 解决的问题是不一样的,虽然一些页面是咱们已经访问过而且常常访问的,可是还有更多的页面是咱们只访问一次,哪怕是这个页面近期已经更新过了但咱们并无再访问。特别是,愈来愈多的应用程序获得很是频繁的更新 —— 例如,Facebook 天天发送几回新的 JavaScript 代码,而且 Twitter、LinkedIn、Google Docs 等状况也会相似。另外,若是你是一个 JS 的开发人员而后发布一个 JavaScript 应用程序 —— 不管是 Web 应用程序仍是其余程序,你老是但愿你和用户之间的第一次接触尽量平滑,这意味着你但愿第一个加载(或更新后的第一次加载)也很是快。

这些问题咱们均可以使用 二进制 AST 解决。

假设

若是咱们提升了缓存会怎样?

额外的技术是要使得浏览器提早抓取和预编译 JS 代码和字节码。

这些技术确实值得研究,也将有助于咱们开发二进制 AST 脚本 —— 每一种技术都改进了另外一种技术。特别是,当使用这种技术时,二进制AST的更好的资源效率将有助于限制资源浪费,同时也改善了这些技术根本不能使用的状况。

若是咱们使用一个现有的 JS 字节码会怎样?

大多数(要不就是全部的)JavaScript 虚拟机已经使用一个内部的 JS 字节码。我彷佛记得至少微软的虚拟机支持特殊的应用使用 JavaScript 字节。

因此,你能够想象一下浏览器厂商将他们的字节码开源而且使全部的 JavaScript 应用使用字节码。这样的话,听起来不是一个好主意,有如下几个缘由:

第一:影响虚拟机的开发者。一旦你暴露本身的内部表示的 JavaScript,你注定要维护它。事实证实,JavaScript 字节码常常变化,以适应新版本的语言或新的优化。强迫虚拟机保持与旧版本的字节码的兼容性将是一个维护和/或性能灾难,因此我怀疑任何浏览器或 VM 供应商都愿意提交这个,除非在很是有限的设置中。

第二:影响 JS 开发者。有几个字节码就意味着维护和运送几个二进制,可能有几十个,若是你想要优化后续版本的浏览器的字节码。更糟糕的是,这些字节码会有不一样的语义,致使不一样语义的 JS 代码编译。虽然这是可能的,毕竟,移动和本地的开发者都是这样作的,这就是在回退 JavaScript。

咱们有一个标准的 JS 字节码会怎样?

因此,若是 JavaScript 虚拟机开发者决定想出一个新的字节码格式,可能做为一个扩展 WebAssembly,但专为 JavaScript 设计呢?

要明确一点:我听到有人后悔没有开发一个这样的格式,但我不知道有人积极致力于此。

没有人这样作的缘由是设计和维护一种随时变化的语言的字节码是至关复杂的,对于一种像 JavaScript 这种已经很复杂的语言来讲,将会更加复杂。最重要的是,持续编译 JavaScript 和对 JavaScript 进行字节颇有可能会失败。这将会产生两个不兼容的 JavaScript 语言,对于 web 有时很是不利。

此外,这样的字节码实际上对代码的大小和性能是否有帮助,还有待论证。

咱们只是让解析器更快会怎样?

那岂不是很好,若是咱们能够让解析器更快?不幸的是,虽然 JS 解析器有了很大改进,但改进的速度是在逐步放缓。

让我引用几个不能跳过或一直有效的步骤:

  • 处理外来编码,标记 Unicode 字节顺序和其余细节;
  • 找出 / 字符,是一个除法操做或者一个注释或正则表达式的开始;
  • 找出 ( 字符,是表达式的开始,一个函数调用的参数列表,箭头函数的参数列表等;
  • 找出这个字符串(分别是字符串模板、数组、函数等)在哪中止,这取决于全部模棱两可的问题;
  • 找出 let a 声明是否和其余的 let avar aconst a 声明冲突,实际上可能在稍后的代码出现;
  • 当遇到使用 eval 时,决定使用 4 个语义中的哪个;
  • 肯定哪些是真正的本地变量;
  • 等等

理想状况下,虚拟机开发者但愿可以并行解析,或延迟直到咱们知道咱们实际上使用的语法在进行解析。事实上,最近的虚拟机实施这些战略。遗憾的是,JavaScript 语法中大量的标记含糊性大大增长了并发性的机会,同时必须抛出对语法错误的限制,从而限制了懒惰解析的机会。遗憾的是,JavaScript 语法中大量模棱两可的标记大大增长了并发性的机会,同时必须抛出对语法错误的限制,从而限制了懒惰解析的机会。

在任何状况下,虚拟机都须要进行昂贵的预分析步骤,可却每每拔苗助长,产生比常规的解析速度较慢,尤为是当应用在压缩编码时。

实际上,二进制 AST 建议旨在克服文本源 JavaScript 的语法和语义所带来的性能限制。

如今是什么状况?

咱们发布这篇博客由于咱们想让你 —— Web 开发人员或者工具开发商必须在尽量早的了解二进制的 AST。到目前为止,咱们从两组收集的反馈是很是好的,咱们期待着与社区密切合做。

咱们已经完成了一个早期的基准测试原型(所以不太实用),正在开发一个先进的原型,不管是对于工具仍是 Firefox,可是咱们还有几个月的时间去作一些有用的事情。

我会在几周内发布更多的细节。

阅读更多:


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOSReact前端后端产品设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索