JavaScript 运行原理
知其然,也要知其所以然,这里主要谈一谈对 JavaScript 运行原理的理解。
JAVA 虚拟机
首先我们从 JAVA 虚拟机说起。
首先说一下为什么要做成虚拟机,因为机器不同,如果没有虚拟机我们就相当于需要针对每一种机器都要进行代码编译,这样肯定是不合理的。所以为了解决这样的问题,Java 引入虚拟机(VM)的概念,让编译后的代码直接跑在一台虚拟的机器上,无论最终的目标平台是什么,都在上面构建出一个虚拟的一致的虚拟机出来,就可以达到一次编译到处执行的效果了。
而从根本上来说 Java 和 C# 不是 Native 语言,编译的结果不是机器指令,而是某种自有的指令格式,自己运行不了,需要专门的程序去解释执行,这个程序就是我们所说的“虚拟机”。
反之,C、C++、Go、Rust 这种语言是 Native 语言,编译出来的结果是机器指令,可以自己直接运行,不存在“虚拟机”的概念。
JavaScript 引擎
而 JavaScript 引擎所做的工作和 JAVA 虚拟机很相似
做了什么
JS 引擎主要是对 JS 代码进行词法、语法等分析,通过编译器将代码编译成可执行的机器码让计算机去执行。
简单来说它的唯一的目的就是读取和编译 JavaScript 代码,也就是说可以分析、解释、优化、垃圾回收 javascript 代码。
执行过程
JavaScript 引擎会加载源代码,把它分解成字符串(又叫做分词),再把这些字符串转换成编译器可以理解的字节码,然后执行这些字节码。
Google 的 V8 引擎是用 C ++ 编写的,它也能够编译并执行 JavaScript 源代码、处理内存分配和垃圾回收。它被设计成由两个编译器组成,可以把源码直接编译成机器码,具体的执行过程可以看成以下几步:
它进行词法分析,就是将源代码分解成一系列具有明确含义的符号或字符串。
然后用语法分析器分析这些符号,将其构建成语法树。
接着四个 JIT(Just-In-Time)进程开始参与进来,分析和执行解析器所生成的字节码。
与 ECMAScript 的关系
准确地讲,每个 JavaScript 引擎都实现了一个版本的 ECMAScript,JavaScript 是它的一个分支。随着 ECMAScript 的不断发展,JavaScript 引擎也不断改进。之所以有这么多不同的引擎,是因为它们每个都被设计运行在不同的 web 浏览器、headless 浏览器、或者像 Node.js 那样的运行时环境中。
JavaScript 引擎是一段程序,我们写的 JavaScript 代码也是程序,如何让程序去读懂程序呢?这就需要定义规则。比如:
var a = 1 + 1
左边 var 代表了这是申明(declaration),它申明了 a 这个变量右边的 + 表示要将 1 和 1 做加法中间的等号表示了这是个赋值语句最后的分号表示这句语句结束了上述这些就是规则,有了它就等于有了衡量的标准,JavaScript 引擎就可以根据这个标准去解析 JavaScript 代码了。那么这里的 ECMAScript 就是定义了这些规则。其中 ECMAScript 62 这份文档,就是对 JavaScript 这门语言定义了一整套完整的标准。其中包括:
var,if,else,break,continue 等是 JavaScript 的关键词 abstract,int,long 等是 JavaScript 保留词怎么样算是数字、怎么样算是字符串等等定义了操作符(+,-,>,< 等)定义了 JavaScript 的语法定义了对表达式,语句等标准的处理算法,比如遇到 == 该如何处理标准的 JavaScript 引擎就会根据这套文档去实现,注意这里强调了标准,因为也有不按照标准来实现的,比如 IE 的 JS 引擎。这也是为什么 JavaScript 会有兼容性的问题。至于为什么 IE 的 JS 引擎不按照标准来实现,就要说到浏览器大战了,这里就不赘述了,自行 Google 之。
所以,简单的说,ECMAScript 定义了语言的标准,JavaScript 引擎根据它来实现,这就是两者的关系。
RunTime
运行时可以简单理解为当前的运行环境,不同的环境提供了不同的 api 调用,如 web 浏览器中的 window 对象,DOM 相关 API 等,这些接口可以提供相关的 JS 调用,