阅读 129

⚙️JavaScript引擎了解下

本文翻译自🚀⚙️ JavaScript Visualized: the JavaScript Engine

作为JavaScript开发者,我们不需要编译自己编写的代码。那么,JavaScript引擎到底怎么处理这些JS代码,转换成机器能懂的东西呢?🥳

注意:本文主要是基于Node.jsV8引擎和基于Chromium内核的浏览器。

正文

通过script标签,HTML解析器识别到javascript代码。

javascript代码要么来自网络,要么来自缓存,或者安装的service worker

请求的脚本会作为字节流,当在下载字节流时,字节流解码器(Byte Stream Decoder)会将其解码。

fisrt_byte_stream_decoder.gif

字节流解码器将从已经解码的字节流中生成tokens。比如,0066解码为f0075解码为u006e解码为n0063解码为c0074解码为t0069解码为i006f解码为o006e解码为n。这就是你输入的function

然后将这些tokens传送到语法分析器(parser)和预语法分析器(pre-parser)。

👀下面的gif图并没有表现出pre-parser,后面会提及到

second_send_toparser.gif

引擎使用两种语法分析器:pre-parseparse。为了减少网站的加载时间,引擎会避免马上分析没必要的代码。

parser会分析立刻需要用到的代码,而pre-parser处理之后将会用到的代码。比如一个函数只有在用户点击按钮才会触发,那就没必要将那段代码立马进行编译以加载网站。

基于字节流解码器传过来的tokens,语法分析器会创建nodes节点,这些节点形成一个抽象语法树Abstract Syntax Tree (AST)🌳。

third_abstract_syntax_tree.gif

接着,Interpreter(解释器)会遍历AST,然后基于AST包含的信息生成字节码字节码生成之后,AST会被删除,对应的内存空间被清理。最后会处理成机器能识别的内容。

fourth_interpreter.gif

虽然字节码运行很快了,但是它可以更快。当字节码运行,相关信息就会生成,它可以检测某些行为是否经常发生,以及使用的数据类型。可能你重复调用一个函数多次:那是时候需要进行优化了,这样会跑得更快!🏃🏽‍♀️

字节码和生成的类型反馈会一起发送到优化编译器。优化编译器获取字节码和类型反馈,并从中生成高度优化的机器码。🚀

fifth_optimizing_compiler.gif

JavaScript是一门动态类型语言,这意味着数据类型可以不停地更改。如果JavaScript引擎必须每次去检查数据类型对应的值,那将会很慢。

为了减少解析代码的时间,优化机器码只处理引擎在运行字节码时见过的情况。如果我们反复使用一段代码,一遍又一遍地返回相同地数据类型,那么可以简单地重复使用经过优化的机器代码以加快处理速度。然而,因为JavaScript是动态类型的。同一片段的代码有可能突然就返回了不同类型的数据。如果发生这种情况,机器码会被进行非最佳化,引擎会回退到解析生成的字节码。

假设一个特定的函数到目前为止,已经被调用了100次并返回相同的值。那么引擎会认为在第101次,你调用它的时候,它也将返回这个值。

假设我们有下面这个求和函数,到目前为止,我们一直以数字作为参数来调用它:

numeric_sum.png

上图两数和返回的是数值3!那么下一次我们调用它,它还会假设我们是通过两个数字类型的数值来调用它。

如果上面假设是真的,那么无需进行动态查找了,重复使用经过优化的机器码即可。否则,上面假设不成功的话,它将恢复为原始的字节码,而不是优化的机器码。

比如,我们下次调用sum函数,传递的参数其中一个是字符串,而不是数字。因为JavaScript是动态类型的,所以我们这样做没什么问题。

string_sum.png

上图代码中,意味着数字2将会被强制转换成字符串类型,函数返回字符串12。引擎已经回到执行解析字节码并更新类型反馈的阶段了。

后话

文章分类
前端
文章标签