01-javascript运行原理小结

258 阅读4分钟

coderwwh w:what是什么? w:why为什么? h:how怎么样?

区别浏览器内核和js引擎

是什么?

www.cnblogs.com/guanghe/p/1…

为什么需要JavaScript引擎?

高级的编程语言都是需要转成最终的机器指令来执行的

事实上我们编写的JavaScript无论你交给浏览器或者Node执行,最后都是需要被CPU执行

但是CPU只认识自己的指令集,实际上是机器语言,才能被CPU所执行

所以我们需要JavaScript引擎帮助我们将JavaScript代码翻译成CPU指令来执行

js如何被浏览器执行?

图片.png

首先html被下载下来

浏览器内核,讲html转换成dom树

渲染树 ---》 布局

在这个执行过程中,HTML解析的时候遇到了JavaScript标签时,停止解析HTML,而去加载和执行JavaScript代码

V8引擎的原理

这里引用一张王老师的图

图片.png

这里的AST和vue将template转换为AST差不多叭

Parse

Parse模块会将JavaScript代码转换成AST(抽象语法树),这是因为解释器并不直接认识JavaScript代码;

如果函数没有被调用,那么是不会被转换成AST的;

Parse的V8官方文档:v8.dev/blog/scanne…

预解析:

所谓的预解析就是:在当前作用域中,JavaScript代码执行之前,浏览器首先会默认的把所有带var和function声明的变量进行提前的声明或者定义。 www.jianshu.com/p/c3276ff58…


Ignition

Ignition是一个解释器,会将AST转换成ByteCode(字节码)

同时会收集TurboFan优化所需要的信息(比如函数参数的类型信息,有了类型才能进行真实的运算);

如果函数只调用一次,Ignition会执行解释执行ByteCode;

Ignition的V8官方文档:v8.dev/blog/igniti…

TurboFan

TurboFan是一个编译器,可以将字节码编译为CPU可以直接执行的机器码;

如果一个函数被多次调用,那么就会被标记为热点函数,那么就会经过TurboFan转换成优化的机器码,提高代码的执行性能;

但是,机器码实际上也会被还原为ByteCode,这是因为如果后续执行函数的过程中,类型发生了变化(比如sum函数原来执行的是

number类型,后来执行变成了string类型),之前优化的机器码并不能正确的处理运算,就会逆向的转换成字节码;

TurboFan的V8官方文档:v8.dev/blog/turbof…

实例分析js执行

var name = "wwh"
var num1 = 20
var num2 = 30

var result = num1 + num2

1.代码被解析, v8引擎内部会帮助我们创建一个对象(GlobalObject -> go)

初始化全局对象

  • js引擎会在执行代码之前,会在堆内存中创建一个全局对象:Global Object(GO)

  • 该对象 所有的作用域(scope)都可以访问

  • 包含Date、Array、String、Number、setTimeout、setInterval等

  • 还有一个window属性指向自己

//伪代码
var globalObject = {
  String: "类",
  Date: "类",
  setTimeount: "函数",
  window: globalObject,
  name: undefined,
  num1: undefined,
  num2: undefined,
  result: undefined
}

执行上下文栈(调用栈)

  • 2.运行代码

 *    2.1. v8为了执行代码, v8引擎内部会有一个执行上下文栈(Execution Context Stack, ECStack)(函数调用栈)

 *    2.2. 因为我们执行的是全局代码, 为了全局代码能够正常的执行,

 * 需要创建 全局执行上下文(Global Execution Context)(全局代码需要被执行时才会创建)

js引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),它是用于执行代码的调用栈

那么现在它要执行谁呢?执行的是全局的代码块

全局的代码块为了执行会构建一个 Global Execution Context(GEC);

GEC会 被放入到ECS中 执行;

GEC被放入到ECS中里面包含两部分内容:

  • 第一部分:在代码执行前,在parser转成AST的过程中,会将全局定义的变量、函数等加入到GlobalObject中,

但是并不会赋值

这个过程也称之为变量的作用域提升(hoisting)

  • 第二部分:在代码执行中,对变量赋值,或者执行其他的函数;

引用王老师的一张图

GEC被放入到ECS中 图片.png

GEC开始执行代码

图片.png