1. 编程语言的执行
高级语言最终都需要编译为低级语言才能被硬件执行,越高级的语言中间的转换时间越长,效率越低,越低级的语言执行素的越快,但是由于缺少高级语言便捷的语法特性所以很难编写代码。
2. 大杂烩JS
它是作者在1995年花10天写出来的,整个语言差不多是很多语言的大杂烩:
- 借鉴了C语言的基本语法;
- 借鉴了Java的数据类型和内存管理
- 借鉴了Scheme语言,将函数作为一等公民
- 借鉴了Self语言,使用了原型继承机制,基于原型prototype的继承机制
所以JS算是 函数式编程 和 面向对象编程的混合产物。
3. 动态语言JS
通过var声明一个变量,可以给这个变量赋值任何数据类型的值,所以称js是一门动态语言。
但这也有一个缺点:我们在声明了复杂数据类型以后,可以随便删除、修改里面的属性,这对开发者来说是很爽的,但是对于编译器来说就是一场灾难,因为源代码里提供的信息太少了,js的语言特性让编译器没办法在运行前知道变量的类型,只有在运行期间才能确定各个变量的类型,这就导致了JS无法在运行前编译出更加迅速的低级语言代码。
但是现在的JS编译引擎都使用了运行时编译,在运行时阶段生成机器代码,而不是提前生成,这就是把代码的运行和生成机器代码同时执行,在运行阶段收集变量的类型信息,然后根据这些信息编译生成机器代码,然后直接使用这些机器代码。像Java这样的语言都是在运行前生成好机器代码。
4. Javascript引擎
JS作为一门高级语言,它被CPU执行之前,需要通过某种程序将js转换成机器语言并执行,这种程序就是Javascript引擎。
Javascript有很多引擎但是在编译JS时流程是差不多的:
- 将js源码通过解析器解析成抽象语法树AST;
- 接着通过接着再通过解释器将AST编译成字节码,字节码与平台无关,可以在任何操作系统上运行
- 字节码最后通过编译器生成机器码,由于不同平台使用 的机器代码会有差异,所以编译器会根据当前平台生成相应的机器码(汇编代码)。
这是一个简化流程,在不同JS引擎中表现会有一定的差异。
5. 什么是V8引擎
V8 是一个接收Javascript代码,并编译代码,执行代码的一个C++程序,编译后的代码可以在多种操作系统,多种处理器上运行。
V8的工作:编译执行js代码、处理调用栈、内存分配、垃圾回收。
6. V8如何编译执行js代码
一般引擎在编译执行js代码都会用到3个重要的组件:解析器、解释器、编译器。
- 解析器:负责将js源代码解析为抽象语法树AST
- 解释器:将AST解释为字节码,同时解释器也有直接解释执行字节码的能力
- 编译器:将字节码编译为运行高效的汇编语言(机器码)
编译流程:
- 由解析器将js解析为抽象语法树AST
- 通过解释器将AST生成字节码,此时AST就被清除了,释放内存空间。生成的字节码直接被解释器执行,同时生成的字节码将作为基准执行模型,
- 在代码不断运行的过程中,解释器会收到很多可以用于优化代码的信息,比如变量的类型,哪些函数执行的频率较高这些信息会收集给编译器,编译器会根据这些信息编译出经过优化的机器代码
注意: 优化后的机器代码也可能会被还原成字节码,因为js时一门动态语言,会导致编译器收集到的变量的类型不一致,比如你一开始声明函数接收的参数时int类型,函数被标记为热点函数后就会生成机器码,但是后边调用函数你传入的参数时String类型,机器码不知道如何处理字符串参数,于是就需要回退到字节码,由解释器来解释执行,效率就会变低。所以我们尽量不要把一个变量的类型变来变去(TS了解一下)。
7. 编译器处理js过程中的优化策略:
- 若函数只是声明,没有被调用,则不被解析生成AST
- 若函数只被调用一次,则生成字节码后就直接被解释执行,不会进行优化编译为机器代码
- 函数若被调用多次,则可能会被标记为热点函数,可能会被编译为机器代码,之后调用这个函数时,直接调用机器代码