0%

JavaScript运行原理

浏览器内核(以webkit为例):WebCore负责解析HTML、布局、渲染等;JavaScriptCore:解析、执行JavaScript代码

ECMAScript3:用这个来学习JavaScript执行、作用域、作用域链、闭包等概念

ECMAScript5:用这个来学习块级作用域、let、const等概念

两者概念描述不一样,整体思路一致

V8引擎的执行原理

V8是用C++编写的开源高性能JavaScript和WebAssembly引擎,可以用于Chrome和Node.js

image-20230323184747752

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,浏览器会对函数进行特殊处理,使得其可以在定义前调用

在代码执行过程中,对变量赋值或者执行其他的函数

全局代码执行前:

image-20230323191418551

全局代码执行后:

image-20230323191040277

函数执行前:

image-20230323191504578

函数执行后:

image-20230323191448418

作用域和作用域链

作用域链:当进入到一个执行上下文时,执行上下文会关联一个作用域链,并根据代码类型,添加一系列的对象

1
2
3
4
5
6
7
8
function foo(age){
function bar(){
console.log(age)
}
return bar
}
var baz = foo(18)
baz()

image-20230323192141405

  • 其作用域链中有两个,分别是foo的作用域、全局作用域

注:作用域链是在函数声明是产生的,与调用时刻无关