浅聊 JS 引擎

浅聊 JS 引擎

初学者的疑问

当我接触前端一段时间后,总是听到别人提到 JS 引擎(如下所指都为以 V8 引擎为例子)。“引擎”二字,颇感高端深奥,对我而言最为直观的就是轰鸣的汽车引擎,当然 JS 的引擎肯定与这个没有什么关系,但凭这二字便觉得其地位一定与引擎于汽车的地位一致。见到这二字的时候,我就在想,引擎是不是就是 JS 的编译器啊,如果是编译器为什么又称之为引擎了,如果不是,两者又有什么区别了。在学习 C 语言的时候,开篇课程没有几节就会讲到说 C 语言编译后会生成二进制代码等,但学习 JS 仿佛很少去提到这些,最多就是说到要下载 node.js,JS 是运行在这环境上的。这篇文章,就来把这些内容给梳理清楚,JS 引擎,编译器,node.js 等几个东西的关系理清楚。

JS 引擎是啥

JS 引擎可以将 JS 代码编译为不同的 CPU(Intel,ARM 以及 MIPS 等)对应的汇编代码(也就是字节码)。同时 JS 引擎的工作不只是编译代码,它还要负责执行代码、分配内存以及垃圾回收。

JS 引擎和 Node.js 以及 Chrome 浏览器又是什么关系了——Chrome 与 Node.js 都是使用了 V8 引擎,或者说 V8 引擎是它们的一部分。最开始我们有一个问题,为什么说学习 JS 的时候第一步需要下载 node.js,其实是这样的,当我们在本地在运行代码的时候,我们需要一个编译器来解释我们的 JS 语言成为对应的机器码,进而让计算机可以读懂并运行,这个编译器就是 V8 引擎,又因为 node.js 使用了 V8 引擎,因此我们需要安装 Node.js。

那么,为什么学习 C 语言的时候没有安装这个环境这一步呢,因为 C 语言是系统级的语言,C 语言可以开发系统和制造环境,而 JS 只能寄生在某个具体的环境中才能运行。

谈到这里不妨在聊聊 JS 语言本身,JS 语言本身是解释型语言,什么又是解释型语言呢?像 C 语言就是编译型语言,其有专门的编译的过程,好处就是之后运行的时候不用再重新编译,直接使用编译的结果就好了(最为明显的例子就是 exe 文件,exe 文件就是编译的结果,这个文件本质上是机器码(二进制),因此计算机可以直接运行)。而解释型语言如 JS 语言,在运行 JS 代码的过程中,就少了这段过程,边执行边翻译,因此效率就会更慢一点。尽管编译型语言如 C 语言因为 其编译后产物是二进制代码,因此可以不断复用,但代码的复用也是局限在相同的环境而言的,因为机器码又回依赖于(硬件和操作系统)运行环境的不同最终的结果也不同(也就是生成的 exe 文件也会不同),因此对不同环境仍然需要编译成不同的可执行文件。

还是举个不太完美的例子,解释语言就像火锅边烫边吃,编译型语言就是炒菜,炒好了一锅出

那为什么不把 JS 引擎称为 JS 编译器呢? 为了提高效率,因此 JS V8 引擎不只完成了编译器的功能还完成了解释器的功能,编译器把源代码转为字节码,解释器又被转化机器码。至于为什么需要先转为字节码,其本质还是为了提高效率。同时正如最开始讲的一样,JS 引擎还要完成其他任务,如垃圾回收。在 V8 引擎中有很多模块,这些部分有对应于 Chrome 的四个模块,:Parser(转源码为抽象语法树),Ignition(解释器,生成字节码),TuborFan(编译器,生成机器码),Orinoco(垃圾回收)。这可能就是不能把 JS 引擎称之 JS 编译器的原因吧。

最后做一个小的总结,也是我的一些感悟,其实,总的来说,无论 JS 亦或者 C++或是 JAVA,这些语言从开始到末尾的整个过程是一样的(从源码到机器码)。C 语言因为先将源码全部编译称机器码,再运行的原因,尽快运行速度加快了但限制了其编译后产物跨平台的使用,JS 作为解释型语言,借助边编译边执行的原因,便使得其只要拥有虚拟环境便可以跨平台执行但其运行速度慢了许多,而 JAVA 借助 JVM 通过中间代码的特点,使得其既具有解释型代码的跨平台和特性,并保持了编译型语言快速(先生成后运行的)的特性。正如上文所讲,V8 引擎生成也生成了中间的代码,但其目的并不是与 JVM 的目的一样,而是通过借助中间代码优化的方式,提高运行效率。

在深入一点,讲讲垃圾回收算法

如果学过 C++语言,一定还记得那时候新建一个列表都要去申请一个空间,而且要预先定义这个空间的大小,如果我们放入的内容过多时候,还需要手动扩大这个空间的大小。但使用 JS 的我们却没有这个烦恼,pop,push,shift,unshift 直接梭哈,根本没有被内存空间大小烦恼过。那为什么 JS 语言不用的考虑这个问题了?因为 JS 的执行环境(或者说 JS 引擎)会自动负责管理代码执行过程中的使用的内存。

先来看看红宝书上,对于垃圾收集原理的一句话讲解:“找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间),周期性地执行这一操作。” 对于垃圾回收机制而言主要是标记清楚和引用计数的方式,这里想聊聊引用技术的原理:

var value={str:'hello'} //value 被引用了一次
var a=value //value被引用2次
var a={str:'bye'}  //value被引用1次

var value=null

demo 中每行代码的注释标注了变量被引用的次数,当变量引用次数为 0 的时候,这个值所占用的内存就会被回收。这里又有个问题,最后一个将变量置为 null,那么是不是意味这变量立刻被回收了。其实并不是的,置为 null,只是切断变量与它此前引用值之间的连接,依然是要等待下次垃圾回收机制的运行。

相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页