JavaScript很酷,但是计算机如何才能理解我们编写的代码?作为JavaScript开发人员,我们不必自己处理编译器。但是,一定要了解JavaScript引擎的基础知识,看看它是如何将我们编写的代码转换为机器可以理解的东西的!
注意:这边文章主要是针对基于V8引擎的Node和Chromium
HTML 解析器遇到 script 标签时,将会从网络,缓存,service worker中获取代码,随后将代码转换为字节流交由字节流解码器进行解码。
字节流解码器从解码的字节流中创建 token。
例如:
- 0066 -> f
- 0075 -> u
- 006e -> n
- 0063 -> c
- 0074 -> t
- 0069 -> i
- 006f -> o
- 006e -> n
解码获得 function 保留关键字,将创建 token 并发送至解析器,以及预解析器,其余字节流将按照这种模式继续解析。
引擎使用了预解析器和解析器。预解析仅仅检查是否有语法错误,这样可以避免解析器在解析时失败。
如果没有发现错误,则解析器根据获取的 token 创建节点,根据这些节点会创建一个 AST抽象语法树。
接下来interpreter遍历AST语法树,生成字节码,字节码生成完毕后将删除AST以节省内存空间。最后字节码交由计算器运行。
字节码执行时,将生成额外的信息用于检测某种行为是否经常发生,以及所使用的数据类型。也许你已经调用了数十次函数,是时候对其进行优化,使其运行的更快了。
字节码与生成的类型反馈一起发送到优化编译器,生成高度优化的机器码
JavaScript是一门动态语言,这意味着数据类型可以不断的变化,如果JavaScript引擎每次必须检查某个变量具有那种数据类型,运行速度将非常慢。
引擎使用一种内联缓存的技术来进行优化,它将代码缓存到内存中,期待将来返回具有相同行为的相同值!假设某个函数被调用了100次,并且到目前为止一直返回相同的值,它将假设在第101次运行时也将返回此值。
假设我们有如下求和函数,我们总是以数值类型的参数来调用他
function sum(a, b) {
return a + b;
}
sum(1, 2);
上述代码将返回数字3,在下次调用他时,他将假设我们再次使用这两个数字作为参数。
如果假设正确的话,则不需要进行动态查找,它可以使用缓存中的值作为返回结果。否则将代码从优化后的机器码转换为原始的字节码。
例如我们下次调用他时传递的参数是sum('1', 2), 数字2将被强制转换为字符串,函数将返回字符串 '12'。类型反馈将被更新。
V8 文档
- V8 Docs
- github.com/v8/v8
- [Chrome University 2018: Life Of A Script]( Chrome University 2018: Life Of A Script)