1.先从Javascript说起
上帝花了7天时间创造了世界,Brendan Eich大神在1995年花了10天时间创造了Javascript。
为什么Javascript被设计为解释型语言?
Javascript的初衷是面向非专业编程人员和设计师的,因此大神认为不应该让这些不专业的人理解编译器这么专业的概念。
解释型语言 vs. 编译型语言
这两个概念其实不太严谨,应该改为“主流实现为解释器的语言”和“主流实现为编译器的语言”更为妥当。事实上很多语言采用的是混合型实现,比如Java需要先编译成字节码,而虚拟机又是解释执行的,执行过程中又会用到JIT即时编译,因此很难说它到底属于哪一种类型的语言。实际上,编译型和解释型最大的区别在于是否保存和复用生成的目标代码(不管是存在磁盘还是内存),编译型会,而解释型则是逐条执行,用完就扔。因此,这里主要讨论编译器和解释器。
编译器的一般实现参见下图:
现代编译器一般可以分为前端和后端:前端主要负责词法分析、语法分析、语义检查等工作,并输出中间代码(为了跨平台)。而后端则负责根据把中间代码转换为目标机器代码,并进行代码优化。所以,编译器的输入是所有代码,最终输出的是目标代码,仅需编译一次,后面就可以多次运行。
解释器则略有不同,一般实现参见下图:
早期的解释器只需要经过词法分析和语法分析,生成抽象语法树AST,就可以送入“树遍历型解释器”执行并输出最终结果了。(esprima网站上可以查看生成的AST结果)
但是后来大家发现,直接解释执行AST这种树形结构效率比较低,如果把AST转换成一种线性结构再执行效果会更好,这就是字节码。所以简单来说,字节码就是AST的后序遍历结果,而执行字节码的程序则被称为虚拟机(VM)。
虚拟机的实现多种多样,最常规的做法就是一行一行地解释执行,但是如果遇到循环的话效率就比较低了,需要一遍又一遍地解释执行。当然,你也可以先编译再执行,但是由于编译优化比较耗时,如果某段代码只需要执行一遍的话,就又得不偿失了。有没有什么办法能结合这两种方式的优点呢?还真有,这就是JIT。