# JavaScript 设计思想
# V8 执行 JavaScript 代码的完整流程
# V8 执行 JavaScript 代码
其主要核心流程分为编译和执行两步。首先需要将 JavaScript 代码转换为低级中间代码或者机器能够理解的机器代码,然后再执行转换后的代码并输出执行结果。
# 高级代码为什么需要先编译再执行
CPU 不能直接识别汇编语言。
虽然汇编语言对机器语言做了一层抽象,减少了程序员理解机器语言的复杂度,但是汇编语言依然是复杂且繁琐的,即便你写一个非常简单的功能,也需要实现大量的汇编代码,这主要表现在以下两点。
首先,不同的 CPU 有着不同的指令集,如果要使用机器语言或者汇编语言来实现一个功能,那么你需要为每种架构的 CPU 编写特定的汇编代码,这会带来巨大的、枯燥繁琐的操作,你可以参看下图:
# 解释执行流程
# 编译执行流程
# 函数申明和函数表达式
上方两处代码,执行的时候 使用函数表达式的 方式,执行会报错。提示 foo is not a function
原因是
# JavaScript 中的 new
function DogFactory(type,color){
this.type = type
this.color = color
}
var dog = new DogFactory('Dog','Black')
上方 new 的对象,对应的 V8 的执行代码
var dog = {}
dog.__proto__ = DogFactory.prototype
DogFactory.call(dog,'Dog','Black')
- 首先,创建了一个空白对象 dog;
- 然后,将 DogFactory 的 prototype 属性设置为 dog 的原型对象,这就是给 dog 对象设置原型对象的关键一步,我们后面来介绍;
- 最后,再使用 dog 来调用 DogFactory,这时候 DogFactory 函数中的 this 就指向了对象 dog,然后在 DogFactory 函数中,利用 this 对对象 dog 执行属性填充操作,最终就创建了对象 dog。
# Java Script 运行时环境
# 宿主环境和 V8 的关系
# 计算机的硬件组织架构
加载到内存中的程序
对应的 汇编
一旦二进制代码被装载进内存,CPU 便可以从内存中取出一条指令,然后分析该指令,最后执行该指令。
我们把取出指令、分析指令、执行指令这三个过程称为一个 CPU 时钟周期。
# 将混乱的 二进制代码转换为有序的指令
# 当一个函数执行,内部压栈状态
# 函数调用结束之后,如何恢复现场(回到上一个函数)
esp 寄存器中保存了栈顶的指针,ebp 寄存器中保存了 调用方法的开始的位置,栈帧中也存储了函数的调用的相关信息,在函数执行结束时,只需要向下移动即可。
# 抽象语法树解析
function foo(a,b) {
var d = 100
var f = 10
return d + f + a + b;
}
var a = 1
var c = 4
foo(1, 5)
对应的抽象语法树
代码解析完成之后,V8 便会按照顺序自上而下执行代码,首先会先执行“a=1”和“c=4”这两个赋值表达式,接下来执行 foo 函数的调用,过程是从 foo 函数对象中取出函数代码,然后和编译顶层代码一样,V8 会先编译 foo 函数的代码,编译时同样需要先将其编译为抽象语法树和字节码,然后再解释执行。
# JS 执行字节码的状态图
# UI 线程处理任务流程
← Egg 集成 Apollo JS 数组整理 →