浏览器内核(以webkit为例):WebCore负责解析HTML、布局、渲染等;JavaScriptCore:解析、执行JavaScript代码
ECMAScript3:用这个来学习JavaScript执行、作用域、作用域链、闭包等概念
ECMAScript5:用这个来学习块级作用域、let、const等概念
两者概念描述不一样,整体思路一致
V8引擎的执行原理
V8是用C++编写的开源高性能JavaScript和WebAssembly引擎,可以用于Chrome和Node.js
Parse:将JavaScript代码转换为AST(抽象语法树)——函数在没调用的时候,不会被转换为抽象语法树
Ignition:将AST转换为字节码
TurboFan:将字节码编译为可以直接运行的机器码
- 如果一个函数被多次调用,就会被标记为热点函数,就会被TurboFan转换为机器码,提高代码的执行性能
- 机器码也会被还原为ByteCode,如果后续执行函数的过程中,类型发生了变化,之前的机器码不能进行准确的运算,就会逆向转换为字节码
JavaScript执行过程
执行上下文栈(ECS):js引擎内部有一个执行上下文栈,是执行代码的调用栈
全局执行上下文:全局代码块为了执行会生成一个全局执行上下文(GEC),并放入ECS中
函数执行上下文:执行到一个函数值,会根据函数体创建一个函数执行上下文(FEC),并放入到ECS中
AO对象:当进入一个函数执行上下文时,会创建一个AO对象,AO对象使用arguments作为初始化,初始值是传入的参数
VO对象:每一个执行上下文都会关联一个VO(Variable Object)对象,变量和函数的声明会被添加到这个VO对象中
注:全局上下文的VO对象就是GO、函数执行上下文的VO对象是AO
初始化全局对象
js在执行代码前,会在堆内存中创建一个全局对象:Global Object(GO)
- 该对象所有的作用域(scope)都可以访问;
- 里面会包含Date、Array、String、Number、setTimeout、setInterval等等;
- 其中还有一个window属性指向自己;
在parser转成AST的过程中,会将全局定义的变量、函数等放入GlobalObject中,但不会赋值(变量的作用域提升)
- JavaScript可以在变量声明前访问,但是值是undefined,浏览器会对函数进行特殊处理,使得其可以在定义前调用
在代码执行过程中,对变量赋值或者执行其他的函数
全局代码执行前:
全局代码执行后:
函数执行前:
函数执行后:
作用域和作用域链
作用域链:当进入到一个执行上下文时,执行上下文会关联一个作用域链,并根据代码类型,添加一系列的对象
1 | function foo(age){ |
- 其作用域链中有两个,分别是foo的作用域、全局作用域
注:作用域链是在函数声明是产生的,与调用时刻无关