写Javascript是一件很酷的事情(不要@我),但是你有没有想过计算机到底是怎么理解你所编写的代码的?只是作为Javascript的开发人员,我们确实没有必要自己处理编译器。但是,简单了解一下Javascript解释器引擎是怎么处理我们编写的JS代码,并转换成能让计算机识别的东西是绝对有好处的。🥳
注意:本篇博客是基于使用V8引擎编写的Nodejs和Chrome内核的浏览器
当HTML解析器从网络(network),缓存(cache),或者是安装的服务端进程(service worker) 里的源代码中解析到script标签,会将请求脚本的响应结果以字节流的形式呈现,字节流解码器负责对其进行解码!当字节流开始下载的时候,字节流解码器开始进行解码
加载自网络,缓存,或者进程中的脚本会以UTF-16字节流的形式传递给字节流解码器
字节流解码器会使用解码的字节流创建标识符(tokens)。比如,0066
解码为f
,0075
解码为u
,006e解码为n
,0063解码为c
,0074
解码为t
,0069
解码为i
,006f
解码为o
,006e
解码为n
后面跟上一个空格。这似乎就是你在js里面写的function
!这个Javascript里面的保留关键字就通过标识符创建出来了,然后发送给解析器(这是预解析器,我还没有在这张gif图里面体现出来,稍后会做解释)。字节流里剩下的标识符通过同样的方式创建。
字节流解码器将字节流转换成相应的标识符,再将这些标识符发送到解析器
JS引擎使用两种解析器:预解析器和解析器。为了减少加载网页的时间,引擎会避免解析没必要立即运行的代码。预解析器处理非立即执行的代码,而解析器处理需要立即执行的代码!如果一个特定的函数只有在用户点击按钮才调用,那么就不必要在加载网页的时候立即编译这个函数。如果用户最后通过点击按钮引入了这个函数,它就会被发送到解析器进行解析。
解析器使用从字节流解码器那里接收到的标识符创建节点。使用这些节点,生成抽象语法树,又叫AST。🌳
解析器基于字节流标识符生成节点,创建抽象语法树
接下来就到了解释程序的时间!解释程序会对AST进行翻译,生成包含AST信息的字节码。当字节码全部生成完毕,AST就会被删除掉,并从内存空间中清除。最终,我们得到计算机能够识别的信息。🎉
字节码很快,但是它还能更快。随着字节码的运行,程序的信息会逐渐生成。它能够检测到某些行为是不是经常发生,以及所使用的的数据类型。可能你已经调用一个函数数十次了:是时候对它进行优化让它能够运行的更快!🏃🏽♀️
字节码会和生成的特定行为的类型反馈一起被发送给优化编译器(optimizing complier)。优化编译器会使用接收到的字节码和特定类型反馈生成高度优化的机器码。🚀
Javascript是一门动态类型语言,这意味着它的数据类型可以随时改变。如果Javascript引擎在每次解释一个特定的值的时候都必须校验它的类型,那么引擎执行就会非常的慢。
为了减少引擎翻译代码的时间,机器码优化程序在运行字节码时只会处理之前遇见过的情况。如果我们要多次重复执行一段返回相同数据类型结果的代码,那么可以简单的重用经过优化的机器码来提升处理速度。然而,由于Javascript是动态类型的,一段相同的代码突然返回不同数据类型的情况是会发生的(例如:一个函数由于传入参数数据类型的不同会导致返回值数据类型的不同)。如果发生这种情况,会得到非优化的机器码,引擎也会回退到解释程序生成的字节码。
假设一个函数调用了100次,并且每次的返回值都是一样的。那么在第101次调用的时候,机器码优化程序会认为也是返回这个值。
让我们看一下下面这个函数,到目前为止的每次调用都是使用数字类型作为参数:
第一次的返回值当然是3
!我们下一次调用它的时候,机器码优化程序会假设我们还是使用两个数字类型的值作为参数调用它。
如果这个假设是正确的的,那么解释程序就没有必要再去动态检查这个函数的返回值类型,只需重用优化过得机器码即可。反之,如果这个假设是错误的,解释程序就会回退使用原始的字节码,而非优化的机器码。
比如,下一次调用的时候,我们传递一个字符串而非数字类型的参数。由于Javascript是动态类型的,这样做并不会有什么错误!
这段代码意味着数字类型的参数2会被将会被强制转换为字符串类型,函数的返回值也会使用字符串"12"替代。机器码优化程序会回退到解释字节码的步骤,并且更新类型反馈。
我希望这篇推文能够对你有帮助!😊当然,还有很多解释引擎的细节在这篇推文中没有覆盖到(JS堆,调用栈,等等),我可能会在后面更新!如果你对Javascript内部原理感兴趣,我相当鼓励你自己去搜索一些相关资料,V8是开源的,并且有很好的文档说明它是如何在后台运行的!🤖
V8 Docs || V8 Github || Chrome University 2018: Life Of A Script