前端开发JavaScript原理:引擎基础

前端开发JavaScript原理:引擎基础

最近,大神 Fabrice Bellard 发布了一个新的JS引擎 QuickJS ,可以将JavaScript源码转换为C语言代码,然后再使用系统编译器(gcc或者clang)生成可执行文件。Facebook为React Native开发了新的JS引擎 Hermes ,用于优化安卓端的性能。它可以在构建APP的时候将JavaScript源码编译为Bytecode,从而减少APK大小、减少内存使用,提高APP启动速度。作为JavaScript程序员,只有极少数人有机会和能力去实现一个JS引擎,但是理解JS引擎还是很有必要的。

一.JavaScript特点

JavaScript是一种广泛用于Web客户端开发的脚本语言,常用来控制浏览器的DOM树,给HTML网页添加动态功能。由于JavaScript提供了丰富的内置函数、良好的对象机制。所以JavaScript还可以嵌入到某一种宿主语言中,弥补宿主语言的表现力,从而实现快速、灵活、可定制的开发。

  • JavaScript是运行时解释语言。
  • 对象模型没有类的概念。
  • 有自动垃圾回收。
  • 弱数据类型。
  • 动态类型(dynamic typing):运行的时候才确定对象的类型。
  • JavaScript没有内置的I/O机制。

JavaScript程序与宿主环境进行交互,是通过一系列预定义的方法和属性实现的,这些方法和属性会再映射成浏览器的内部原生代码,所以与其他很对常规的编程语言不同,浏览器开放的这些借口往往受限且有针对性。

二.JavaScript引擎

JavaScript引擎是一个专门处理JavaScript脚本的虚拟机,一般会附带在网页浏览器之中。

什么是虚拟机?
虚拟机(英语:virtual machine),在计算机科学中的体系结构里,是指一种特殊的软件,可以在计算机平台和终端用户之间创建一种环境,而终端用户则是基于这个软件所创建的环境来操作软件。
根据虚拟机的运用和直接机器的相关性分为两类:
- 系统虚拟机,提供一个可以运行完整操作系统的完整系统平台。
- 程序虚拟机,运行单个计算机程序设计,这意谓它支持单个进程。

JavaScript引擎就是一种程序虚拟机。

简单地说,JavaScript引擎能够“读懂”JavaScript代码,并准确地给出代码运行结果的一段程序。如 var a = 1 + 2; JavaScript引擎的作用就是(解析)这行代码,并且将a的值变为3。

学过编译原理的人都知道,对于静态语言来说(如Java、C++、C),处理上述这些事情的叫 编译器(Compiler) ,相应地对于JavaScript这样的动态语言则叫 解释器(Interpreter) 。两者的区别:

  • 编译器是将源代码编译为另外一种代码(比如机器码,或者字节码)
  • 解释器是直接解析并将代码运行结果输出 。 比方说,firebug的console就是一个JavaScript的解释器。
编译型语言:程序在执行之前需要一个专门的编译过程,把程序编译成为机器语言的文件(即exe文件),运行时不需要重新编译,直接用编译后的文件(exe文件)就行了。
优点:执行效率高
缺点:跨平台性差
解释型语言:程序不需要编译,程序在运行的过程中才用解释器编译成机器语言,边编译边执行(没有exe文件)。
优点:跨平台性好
缺点:执行效率低

但是,现在很难去界定说,JavaScript引擎到底算是解释器还是编译器,因为,比如像V8(Chrome的JS引擎),它其实为了提高 JS的运行性能,在运行之前会先将JS编译为本地的机器码(native machine code),然后再去执行机器码(这样速度就快很多),相信大家对 JIT(Just In Time Compilation) 一定不陌生吧。

使用 JavaScript 代码可以修改网页的内容,也能修改 CSS 的信息,JavaScript 引擎能够解释 JavaScript 代码,并且通过 DOM 接口和 CSSOM 接口来修改网页内容和样式信息,从而改变渲染结果。

附加: JavaScript 比Java慢的原因
和大多数解释型语言一样,JavaScript运行也比较慢,和Java等静态编译语言相比,究其原因大概有:
- JavaScript变量无类型信息,不能做偏移信息查找,偏移信息共享等编译阶段的优化
- JavaScript将源码编译为字节码的过程要占用运行时间,而Java的编译则是开发阶段,不占用任何运行时间。故Java可以尽可能的在编译阶段做优化

三.JavaScript引擎组成

  1. 编译器。主要工作是将源代码编译成抽象语法树,然后在某些引擎中还包含将抽象语法树转换成字节码。
  2. 解释器。在某些引擎中,解释器主要是接受字节码,解释执行这个字节码,然后也依赖来及回收机制等。
  3. JIT工具。一个能够JIT的工具,将字节码或者抽象语法树转换成本地代码,当然它也需要依赖牢记
  4. 垃圾回收器和分析工具(profiler)。它们负责垃圾回收和收集引擎中的信息,帮助改善引擎的性能和功效。
附加知识:
推动JavaScript运行速度提高的另一大利器就是JIT技术,其作用是解决解释性语言的性能问题,主要思想是当解释器将源代码解释成内部表示的时候(类似于java字节码),JavaScript的执行环境不仅是解释这些内部表示,而且将其中一些字节码(使用率高的部分)转成本地代码(汇编代码),这样就可以被CPU直接执行,而不是解释执行,从而提高性能。

四.JavaScript引擎和渲染引擎的关系

网页的工作过程需要两个引擎,渲染引擎和JavaScript引擎。JavaScript引擎负责执行JavaScript代码,渲染引擎负责渲染网页。JavaScript引擎提供调用接口给渲染引擎,以便让渲染引擎使用JavaScript引擎来处理JavaScript代码并获取结果。JavaScript引擎需要能够访问渲染引擎构建的DOM树,所以JavaScript引擎通常需要提供桥接的接口,渲染引擎根据桥接接口来提供让JavaScript访问DOM的能力。

渲染引擎和JavaScript引擎之间的调用关系:


两种引擎通过桥接接口来DOM结构,造成了性能的损失。所以目前为止使用JavaScript操作DOM还是一个非常低效率的事。目前主流的解决方案是使用虚拟DOM的方式。

附加.

A.知名JavaScript引擎

  • V8(Google),用C++编写,开放源代码,Google (丹麦)研发小组在 2006 年开始研发 V8 ,部分的原因是 Google 对既有 JavaScript 引擎的执行速度不满意, 在2008年推出chrome,也用于Node.js, 巨大的速度优势, 迅速占领市场. 2017年chrome的市场占有达到59%。
  • JavaScriptCore(Apple),开放源代码,用于webkit型浏览器,如Safari ,2008年实现了编译器和字节码解释器,升级为了SquirrelFish。苹果内部代号为“Nitro”的JavaScript引擎也是基于JavaScriptCore引擎的。
  • Rhino,由Mozilla基金会管理,开放源代码,完全以Java编写,用于HTMLUnit
  • SpiderMonkey(Mozilla),第一款JavaScript引擎,早期用于Netscape Navigator,现时用于Mozilla Firefox。
  • Chakra (Microsoft)(JScript引擎),用于Internet Explorer。
  • Chakra (Microsoft)(JavaScript引擎),用于Microsoft Edge。
  • KJS,KDE的ECMAScript/JavaScript引擎,最初由哈里·波顿开发,用于KDE项目的Konqueror网页浏览器中。
  • IOT: duktape JerryScript

B.JavaScript解析引擎与ECMAScript是什么关系?

JavaScript引擎是一段程序,我们写的JavaScript代码也是程序,如何让程序去读懂程序呢?这就需要定义规则。比如,之前提到的 var a = 1 + 1; ,它表示:

  • 左边var代表了这是申明(declaration),它申明了a这个变量
  • 右边的+表示要将1和1做加法
  • 中间的等号表示了这是个赋值语句
  • 最后的分号表示这句语句结束了

上述这些就是规则,有了它就等于有了衡量的标准,JavaScript引擎就可以根据这个标准去解析JavaScript代码了。那么这里的ECMAScript就是定义了这些规则。其中ECMAScript 262这份文档,就是对JavaScript这门语言定义了一整套完整的标准。其中包括:

  • var,if,else,break,continue等是JavaScript的关键词
  • abstract,int,long等是JavaScript保留词
  • 怎么样算是数字、怎么样算是字符串等等
  • 定义了操作符(+,-,>,<等)
  • 定义了JavaScript的语法
  • 定义了对表达式,语句等标准的处理算法,比如遇到 == 该如何处理
  • ⋯⋯

标准 的JavaScript引擎就会根据这套文档去实现,注意这里强调了 标准 ,因为也有不按照标准来实现的,比如IE的JS引擎。这也是为什么JavaScript会有兼容性的问题。至于为什么IE的JS引擎不按照标准来实现,就要说到浏览器大战了,这里就不赘述了,自行Google之。

所以,简单的说,ECMAScript定义了语言的标准,JavaScript引擎根据它来实现,这就是两者的关系。

编辑于 2019-09-28 13:43