在了解之前,我们首先要知道几个概念:编译器、解释器、抽象语法树(AST)、字节码、即时编译器(JIT)等
1. 编译器和解释器
之所以存在编译器和解释器,是因为机器读不懂程序员所写的高级语言代码,所以在执行代码之前,需要将代码“翻译”成机器能读懂的机器代码。
然而语言又可分为编译型语言和解释型语言。
两种语言的执行过程可大致如下:
- 编译型语言在执行时,编译器会先对源代码依次进行词法分析、语法分析,然后生成抽象语法树(AST)。在AST基础上进行词义分析生成中间代码并进行代码优化,最后会生成一个二进制的可执行文件。
- 解释型语言在执行时,解释器也会对源代码依次进行词法分析、语法分析,然后生成抽象语法树(AST)。不过他会在AST基础上进行词义分析生成字节码(字节码就是介于 AST 和机器码之间的一种代码。但是与特定类型的机器码无关,字节码需要通过解释器将其转换为机器码后才能执行),最后根据字节码,解释器和编译器会相互配合对字节码进行边编译成机器代码边执行。
2. 具体V8是怎么执行的呢
2.1 生成抽象语法树(AST)和执行上下文
AST:编译器和解释器都很难理解开发者的高级语言,他们能理解的就是AST了。AST是一种非常重要的数据结构,可以将其看成代码的结构化表示。
将源代码转化成抽象语法树,并生成执行上下文。
- 分词(词法分析)scanner:将源码拆解成一个个不能再分的词法单元(token)
- 解析(语法分析)parser:将上一步生成的token数据根据语法规则转换成AST。
2.2 生成字节码
有了AST和执行上下文后,解释器会根据AST生成字节码,并解释执行字节码。
为什么不直接生成机器码,而是中间生成一个字节码呢?
其实一开始V8确实是直接生成机器码。但早年内存都比较小,生成机器码的话非常占用内存。为了解决内存占用问题,才引入了字节码。
2.3 解释执行字节码
在执行字节码过程中,如果这段字节码第一次执行,则解释器会逐条解释执行。但是在执行字节码过程中,发现有热点代码(HotSpot,比如一段代码被重复执行多次,则被称为热点代码),那么后台的编译器就会把该热点代码编译为高效的机器码,然后当再次执行热点代码时,只需要执行编译后的机器码就可以了,这样就大大提高了代码的执行效率。
这种字节码配合解释器和编译器的技术,称其为即时编译(JIT)
因此可以说,当V8执行时间越久,执行效率越高,这是因为被转化为的机器码越来越多,执行效率越来越高效。(但如果说有没有什么弊端,个人觉得内存的占用会越来越大)
3. JavaScript的性能优化
对于优化JavaScript的执行效率,更应将优化的中心聚焦在:
- 提升单次脚本的执行速度,避免JavaScript的长任务霸占主线程,可以使得页面快速响应交互
- 避免大的内联脚本,因为在解析html的过程中,解析和编译也会占用主线程
- 减少JavaScript文件的容量,因为更小的文件会提升下载速度,并且占用更低的内存。
|