likes
comments
collection
share

再学JS高级笔记1:JS运行原理

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

1 JavaScript运行原理

1.1 浏览器的工作原理

JavaScript代码在浏览器中是如何被执行的?再学JS高级笔记1:JS运行原理

1.2 浏览器的内核

那么,向服务器请求了相关文件并下载了之后,是什么来解析我们的js文件的呢?答案是浏览器内核

实际上,我们经常说的浏览器内核指的是浏览器的排版引擎:

  • 排版引擎(layout engine)也称为浏览器引擎(browser engine)、页面渲染引擎(rendering engine)或样板引擎
  • 不同的浏览器有不同的内核

    • Gecko
    • Trident
    • Webkit
    • Blink

1.3 浏览器渲染过程

再学JS高级笔记1:JS运行原理

如果在执行这个过程中,HTML解析的时候遇到了JavaScript标签,应该怎么办呢?

  • 会停止解析HTML,而去加载和执行JavaScript代码

那么JavaScript代码由谁来执行呢?

  • JavaScript引擎

1.4 JavaScript引擎

为什么需要JavaScript引擎?

  • 高级的编程语言都需要转成最终的机器指令来执行
  • 实际上我们编写的JavaScript无论是交给浏览器或者Node执行最后是都需要被CPU执行的
  • CPU只认识自己的指令集,只有机器语言才能被CPU所执行
  • 所以需要JavaScript引擎帮助我们将JavaScript代码翻译成CPU指令才能被执行

常见的JavaScript引擎:

  • SpiderMonkey
  • Chakra
  • JavaScriptCode
  • V8:Google开发的JS引擎,帮助Chrome从众多浏览器中脱颖而出

1.5 浏览器内核和JS引擎的关系

以WebKit为例,WebKit事实上由两部分组成:

  • WebCore:负责HTML解析、布局、渲染等等相关的工作
  • JavaScriptCore:解析、执行JavaScript代码

再学JS高级笔记1:JS运行原理

小程序也是类似的,将代码分为两层:

  • Webview渲染层:负责渲染HTML结构等
  • JsCore逻辑层:负责解析执行JavaScript代码

再学JS高级笔记1:JS运行原理

1.6 V8引擎

官方的定义:

  • V8是用C++编写的Google开源高性能JavaScript和WebAssembly引擎,它用于Chrome和Node.js等
  • 可以在很多不同的操作系统上运行
  • V8可以独立运行,也可以嵌入到任何C++应用程序中

V8引擎本身的源码非常复杂,大概有超过100w行C++代码,通过了解它的架构,我们可以知道它是如何对JavaScript执行的:

再学JS高级笔记1:JS运行原理

  1. Parse模块:进行词法分析和语法分析,将代码转换成<u>AST(抽象语法树)</u>,因为解析器并不直接认识JS代码
  1. Ignition解释器:会将AST转换成ByteCode(字节码)
  • 同时会收集TurboFan优化所需要的信息(比如函数参数的类型信息,有了类型才能进行真实的运算)
  • 如果函数只调用一次,Ignition会执行解释去执行ByteCode
  • pIgnition的V8官方文档:https://v8.dev/blog/ignition-interpreter
  • 字节码是可以跨平台的,所以当运行的时候cpu会将字节码转成相关平台的汇编代码,再转换成cpu指令
  1. TurboFan编译器:可以将字节码编译为CPU可以直接执行的机器码
  • 如果一个函数被多次调用,会被标记为热点函数,那么就会经过TurboFan转换成优化的机器码,提高代码的执行性能
  • 但是机器码实际上也会被还原为ByteCode。这是因为如果后续执行函数的过程中,类型发生了变化(比如sum函数原来执行的是number类型,后来执行变成了string类型),之前优化的机器码并不能正确地处理运算,就会逆向地转换成字节码(Deoptimization)
  • TurboFan的V8官方文档:https://v8.dev/blog/turbofan-jit

1.7 V8执行过程的细节

再学JS高级笔记1:JS运行原理

JavaScript源码是如何被解析(parse过程)的呢?

  1. Blink(内核)将JS代码交给V8引擎,Stream获取到JS代码并且进行编码转换
  2. Scanner(扫描器)会进行词法分析(lexical analysis),词法分析会将代码转换成tokens
  3. 接下来tokens会经过ParserPreparser,被转换成AST树
  • Parser就是直接将tokens转成AST树架构
  • PreParser称为预解析,为什么需要预解析呢?

    • 因为不是所有的JavaScript代码在一开始时就会被执行,那么对所有代码进行解析必然会影响网页的运行效率
    • 所以V8引擎就实现了Lazy Parsing(延迟解析)的方案,它的作业是将不必要的函数进行预解析,也就是只解析暂时需要的内容。而对函数的全量解析是在函数被调用时才会进行
  • 生成的AST树只会被Ignition转成字节码
  • 之后的过程就是代码的执行过程