全部 / 前端 / 技术 · 2022年11月22日 0

JavaScript 视觉化:JavaScript 引擎

JavaScript 很酷😎,但是机器是如何真正读懂你所写的代码?作为一名 JavaScript 开发者,我们通常是不需要自己处理编译的。然而,了解 JavaScript 引擎的基础知识,看看它是如何处理我们的对人类友好的 JavaScript 代码,并将其转化为机器能够理解的东西,绝对是一件好事!

注意:这篇文章以 Node.js 和 Chromium 为核心使用的 V8 为基础来讲解的。

HTML 解析器在源码中遇到 script 标签,源码可能会从网络、缓存或者 service worker. 中加载,相应的脚本会作为字节流,通过字节流解码器来处理!字节流解码器对正在下载的字节流进行解码。

1

字节流解码器从已经解码的字节中创建 tokens,例如:0066 解码为 f, 0075 为 u, 006e 为 n, 0063 为 c, 0074 为 t, 0069 为 i, 006f 为 o。006e 解码为 n 其后跟着一个空格,就像你写了一个 function!这是 JavaScript 的保留字,创建了一个 token 接着发送到解析器(以及预解析器,但在 gif 图中没有表示出来,后面会做解释),剩下的字节流也会按照这个流程进行。

2

引擎使用了 2 中解析器:预解析器和解析器。为了减少加载网页的时间,引擎不会解析现在不需要的代码。当解析器正在处理立即需要的代码时,预解析器可能后续才会派上用场。若一个函数只在用户点击按钮后才调用,那在加载网页时并不需要立即编译那些代码。若用户最终点击了按钮然后需要那段代码时,它就会被发送到解析器。

解析器以从字节流解码器获取的 token 为基础来创建节点,这些节点就构成了 Abstract Syntax Tree, 或 AST. 🌳

111

接下来,轮到解释器遍历整个 AST 然后通过 AST 包含的信息创建字节码。一旦字节码全部生产,AST 就会被删除同时清除所占的内存空间,最后,我们得到了机器可以识别的内容。

22

虽然字节码已经很快,但它还可以更快。当字节码运行的时候,一些信息就会生成,浏览器可以检测某些行为是否经常发生,以及被使用的数据类型。或许你已经调用某个方法很多次:是时候优化它了,所以它会执行的更快!

字节码和生成的类型反馈,会被送到一个优化编译器。它通过字节码和类型反馈信息,生成优化程度更高的机器码。

5

JavaScript 是一个动态类型的语言,意味着数据的类型会经常变动。若 JavaScript 引擎每次都要检测该数据类型是否包含合适的值将会极其的慢。

为了减少解析代码的时间,优化的机器码只处理引擎在运行字节码时曾遇到的情况。如果我们使用了一块始终返回同样数据类型的代码片段,为了加快速度,优化的机器可以被简单的使用。然而,由于 JavaScript 是动态类型的,可能会发生同是一块代码返回不同类型的数据。若发生,优化的机器码将会失效,接着引擎回退到解释生成的字节码。

话说若一个特定的函数被调用了 100 次都返回相同的值,引擎将会假设它在 101 次时还会返回同一个值。

我们假设有如下 sum 函数,一直通过数字类型的参数调用:

返回值是 3,下次我们再调用的时候,引擎会假设我们依旧使用数字类型的值来调用。

若真是那样,不需要动态类型的检查,引擎只使用优化的机器码就行。然而,若假设失败,引擎会回退到起初的字节码替代优化的机器码。

例如,下次我们调用它时,我们传入了一个字符串而不是数字,由于 JavaScript 是动态类型的,我们可以这样做并不会报错:

意味着 2 会被强制转化为字符串,而该函数会返回字符串 12。引擎会返回解释执行字节码和跟新类型反馈。