V8引擎的定义
v8引擎是c++编写的,有谷歌开源的高性能JavaScript和WebAssembly引擎
它实现了ECMAScript和WebAssembly,并在Windows 7或更高版本,macOS 10.12+和使用 x64,IA-32,ARM或MIPS处理器的Linux系统上运行
V8可以独立运行也可以嵌入到任何C++应用程序中
javascript代码 ===> V8引擎 ====> 目标 cpu
首先javascript代码通过parse(有解析的意思)模块,进行词法分析和语法分析生成ast抽象语法树
如果是ts代码(babel进行转换),就会将ts代码 ===> ts对应的ast语法树 ===> 修改后的新ast抽象语法树 ===> generate code ===> js代码
vue template ===> ast抽象语法树 ===> createVNode(创建虚拟DOM节点)
例如:
const name = 'why';
parse模块
词法分析:
他会对这行代码的每一个词进行切割(相当于一个切割),最后会生成恒多个tokens:[{type: 'keyword', value: 'const',}, {type: 'identifier', value: 'name'},......],也可以把它看成一个json,keyword是因为解析到const时发现是个关键字,所以type值为keyword,并且会给value赋值const,当解析到name时,发现是个标识符,所以type为identifier,value为name,一直这样进行词法分析,包括最后的分号都会进行解析,就这样通过type划分了一个一个的类型,通过词法分析的一个个类型分别进行语法分析
语法分析:
根据不同类型进行生成对应的ast抽象语法树
之所以转成抽象语法树是因为里面的属性都是固定的,无论以后做什么操作,都很方便,
比如说现在这个抽象语法树我就可以转成es5的代码,也可以转成我想要的字节码(bytecode),而转换成字节码是由V8的ignation来进行操作
ignation模块(解释器/转化器)
作用:将ast抽象语法树转换成字节码(bytecode) 如果函数未调用,是不会转换成ast的
疑惑:为什么不直接转换成机器码呢?
因为当前程序运行环境可能不一样的,可能跑在mac本的Chrome浏览器上,也有可能跑在window本的Chrome浏览器上,也有可能跑在服务器linux的电脑的浏览器或node上,不同的环境有不同的cpu,不同的cpu可能又有不同的架构,不同的架构所执行的机器指令是不一样的(就不深入了,没实力)
解惑:由于存在环境可能不一样,所以很难知道到底转换成哪种机器指令(和java很像),所以这里将我们的代码转换成字节码(可以实现跨平台)
当要运行的时候,V8引擎会将字节码转换成对应的机器指令,字节码转换成机器码肯定比ast抽象语法树转换成字节码性能要好
字节码 ===> 汇编代码 ===> cpu要执行的机器指令
等等,还没完,还漏了一个TurboFan模块
TurboFan模块
觉不觉得字节码转换成cpu对应的机器指令也很费性能,比如一个函数,多次调用,我就要多次转换,一样的工作,重复执行,所以,TurboFan模块就来解决问题了
如何解决:
将我们多次调用的函数直接从字节码转换成对应的cpu机器指令并保存起来,等到被调用时直接执行指令,性能直接拉满(只执行少次的代码就不用进行转化了,浪费空间资源)
那么问题来了,它怎么知道这个函数被多次执行了?
是由ignation模块来进行收集信息,比如函数的执行信息,如果发现函数执行评频率非常高,就会把这个函数进行标记成hot(热函数),一旦是热函数就会由TurboFan模块进行转换成优化后机器指令(MachineCode)
不过这样还是有问题的
比如说这个函数是求和函数
function sum(num1, num2) {
return num1 + num2
}
sum(20, 20)
sum(30, 30)
......
sum('20', 30)
本来是数字相加的,突然传来了一个字符串,字符串和数字相加是不同的指令的,又由于JavaScript是一门动态语言,不会对类型做检测,所以一开始是检测不到数据类型的变化的,导致最后那个字符串类型函数运行时机器指令不能用,就会通过Deoptimizayion(反向优化),把机器指令转换成字节码,在按照字节码的方式进行运行结果,
所以TypeScript代码比我们平时写的JavaScript代码性能稍微高一点