likes
comments
collection
share

浏览器执行JavaScript原理-v8引擎

作者站长头像
站长
· 阅读数 11

前言

了解浏览器运行JavaScript代码的原理,有助于我们开发中快速解决问题;本章主要讲解浏览器常见的内核、JavaScript引擎以及执行js代码的原理。


一、浏览器的内核与JavaScript引擎

1.认识常用浏览器的内核

排版引擎,也称浏览器引擎、页面渲染引擎或样式引擎

Gecko:早期被Netscape和Mozila Firefox浏览器使用; Trident::微软开发,被IE4-IE11浏览器使用,Edge浏览器已经转向Blink; Webkit::苹果基于KHTML开发的(开源),用于Safari,Google Chrome之前的版本也有使用; Blink:是Webkit的一个分支,Google开发,目前用于Google Chrome、Opera、Edge等;

2.浏览器渲染过程

  1. 加载html,将html文件转成domTree
  2. 执行过程中,html解析的时候遇到了JavaScript标签,会停止解析html,而去加载和执行JavaScript代码
  3. 加载并解析css,生成Style Rules
  4. 将Style Rules 与 DOM Tree相结合
  5. 通过布局引擎形成Render Tree
  6. 绘制、展示

浏览器执行JavaScript原理-v8引擎

3.认识JavaScript引擎

为什么需要JavaScript引擎? 实际上高级编程语言都是需要转成机器指令来执行的,JavaScript也不例外。 我们所编写的JavaScript程序无论是在浏览器或者Node环境下,最终都是需要被CPU执行的;CPU只认识自己的指令集、即机器语言。所以我们需要JavaScript引擎帮助我们将JavaScript代码翻译成CPU可执行的指令。

常见的JavaScript引擎:

  • SpiderMonkey:第一款JavaScript引擎,由Brenden Eich(即JavaScript作者)
  • Chakra:微软开发,用于IE浏览器
  • JavaScriptCore:WebKit中的JavaScript引擎,Apple公司开发
  • V8:Google开发的强大JavaScript引擎,也帮助Google Chrome从众多浏览器中脱颖而出

4.浏览器内核与JavaScript引擎的关系

WebKit
WebCore负责HTML解析\布局\渲染等等相关工作
JavaScriptCore:解析和执行javasc代码

二、V8引擎

1.原理

我们来看一下官方对V8引擎的定义:

  • V8是用C ++编写的Google开源高性能JavaScript和WebAssembly引擎,它用于Chrome和Node.js等。
  • 它实现ECMAScript和WebAssembly,并在Windows 7或更高版本,macOS 10.12+和使用x64,IA-32,ARM或MIPS处理器的Linux系统上运行。
  • V8可以独立运行,也可以嵌入到任何C ++应用程序中。 浏览器执行JavaScript原理-v8引擎

抽象语法树:astexplorer.net/

2.执行的细节

那么我们的JavaScript源码是如何被解析(Parse过程)的呢?

  1. Blink将源码交给V8引擎,Stream获取到源码并且进行编码转换;
  2. Scanner会进行词法分析(lexical analysis),词法分析会将代码转换成tokens;
  3. 接下来tokens会被转换成AST树,经过Parser和PreParser:
  4. Parser就是直接将tokens转成AST树架构; PreParser称之为预解析,为什么需要预解析呢? 这是因为并不是所有的JavaScript代码,在一开始时就会被执行。那么对所有的JavaScript代码进行解析,必然会影响网页的运行效率; 所以V8引擎就实现了Lazy Parsing(延迟解析)的方案,它的作用是将不必要的函数进行预解析,也就是只解析暂时需要的内容,而对函数的全量解析是在函数被调用时才会进行; 比如我们在一个函数outer内部定义了另外一个函数inner,那么inner函数就会进行预解析;
  5. 生成AST树后,会被Ignition转成字节码(bytecode),之后的过程就是代码的执行过程(后续会详细分析)。

三、js执行原理

流程图:

浏览器执行JavaScript原理-v8引擎

以下面这段代码举例

var name = 'ant'
function foo(){
	var name = 'foo'
	console.log(name)
}
var num1 = 20var num2 = 30
var result = num1 + num2;
console.log(result);

1.代码被解析

  • v8引擎内部会帮助我们创建一个对象(Global Object window就是全局);
  • 找变量声明,将变量声明作为GO对象的属性名,值赋予undifined
  • 找全局里的函数声明,将函数名作为GO对象的属性名,值赋予函数体 浏览器执行JavaScript原理-v8引擎
// 1.代码被解析,v8引擎内部会帮助我们创建一个对象
//伪代码
globalObject = {
	Array: '类',
	String: "类",
	Date: '类',    
	setTimeout: '类',
	window: globalObject,
	num1: undefined,
	num2: undefined,
	result: undefined
}
  1. 运行代码 Js引擎内部有一个执行上下文栈(Execution Context Stack,简称ECS),它是用于执行代码的调用栈。
    • 为了全局代码能够正常执行,需要创建全局执行上下文栈(Global execution context) GEC会 被放入到ECS中 执行;
      • 第一部分:在代码执行前,在parser转成AST的过程中,会将全局定义的变量、函数等加入到GlobalObject中,但是并不会赋值;这个过程也称之为变量的作用域提升(hoisting)
      • 第二部分:在代码执行中,对变量赋值,或者执行其他的函数;
    • 在执行的过程中执行到一个函数时,就会根据函数体创建一个函数执行上下文(Functional Execution Context,简称FEC),并且压入到EC Stack中。
      • 第一部分:在解析函数成为AST树结构时,会创建一个Activation Object(AO): AO中包含形参、arguments、函数定义和指向函数对象、定义的变量;
      • 第二部分:作用域链:由VO(在函数中就是AO对象)和父级VO组成,查找时会从里到外一层层查找;
      • 第三部分:this绑定的值:这个后面再单独写一篇讲吧! 浏览器执行JavaScript原理-v8引擎
  2. 运行完成 ESC中执行过的函数AO会弹出栈(销毁AO)