重学Js(零): 1、JS代码是如何运行的?

404 阅读4分钟

前言

  • 细阅此文章大概需要 10分钟左右

  • 本篇中简要讲述了如下内容:

    1. JS是一门编程语言
    2. V8
    3. js 代码编译过程
  • 如果有任何问题都欢迎指正,我看到了就会回复/修改,如果我解决不了也可以一起探讨、学习。希望今后能和大家共同学习、进步。

  • 下一篇会尽快更新,已经写好的文章也会在今后随着理解加深或者加入一些例如图解 代码块 并且断断续续的进行修改。

  • 如果觉得这篇文章对您有帮助,您的点赞就是对我创作的最大激励

js 是一门编程语言

从编辑语言角度来看 可以分为三大类

  • 机器语言: 10101001010 一些机器指令
  • 汇编语言: bx 一些汇编指令
  • 高级语言: go dart rust kotlin

而高级语言中也可以分为两大类:

  • 编译性语言: C C++这一类语言就是编译过后变成可执行文件 随后计算机进行读取执行
  • 解释性语言: js python 机器边读取源代码边进行解释 随后计算机进行执行
  • (Java是啥类型语言由屏幕前的您来决定)

高级语言都是需要进行转化为机器指令来执行的 同时我们编写的 js 代码最后都是交付给 cpu 执行的 但是 cpu 只认识自己的指令集 实际上是机器语言 才能被 cpu 执行

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

浏览器内核和 js 引擎的关系: 由 webkit 为例 浏览器内核(webkit) = js 引擎(jscore)+布局引擎(webcore)

目前市面上JS引擎有很多 如JavaScriptCore、V8、SpiderMonkey 其中有一定代表意义的JS引擎就是V8,在V8出现之前所有JS虚拟机采用解释执行的原因 而V8采用了即时编译(JIT)的双轮驱动的设计模式

V8

V8 介绍

V8 是用 C++编写的 Google 开源高性能 javascript 和 webAssembly 引擎 它用于 Chrome 和 nodejs 它实现 ECMAScript 和 WebAssembly 同时 V8 可以独立运行也可以嵌入到任何 C++应用中

上面这段是维基百科上的原话我直接CV过来了

所以我们可知 目前咱们每天写的js代码是是通过V8编译 同时js是寄生于V8

js 代码编译过程

下图为V8编译流水线

image.png

  1. js 代码通过 V8 的 Parse 模块 解析为 AST 抽象语法树 这一部分的核心就是对 例如 var redux 所有的 js 语句进行 词法分析 & 语法分析 词法分析就是对每一个词进行扫描 一般就是 js 关键字 变量名 值 最后生成海量的 tokens 这些 tokens 会以一种数组对象方式进行存储 [{},{}] 比方说上面语句经过词法分析后的结构就是 tokens:

    [
      { type: "keyword", value: "var" },
      { type: "identifier", value: "redux" },
    ];
    

    语法分析主要是生成 AST 语法抽象树 如果函数没有被调用 那么是不会被转换为 AST 的 在线 js 代码 AST 抽象语法树 astexplorer.net/

  2. AST 抽象语法树通过 V8 内置 Ignition 模块 解释/转化 将 AST 抽象语法树转化为字节码 (bytecode) 这些字节码是跨平台的 无关各种系统 CPU 架构 字节码会转为不同平台上的指令 最后由 CPU 去运行结果 执行结果也会随之对应反映到我们浏览器中 同时也会收集 TurboFan 优化所需的信息(函数参数类型 有了类型才能进行技术)

  3. V8 引擎有自己独特的性能优化的方面 如果每次字节码都转化为 CPU 指令集 显然这一步将耗费很多性能 所以 V8 引擎采用了一种类似 Redis 缓存 的功能 其实现就是内置模块 TurboFan

  4. TurboFan 的核心功能就是搜集函数的执行信息(例如类型) 如果发现当前函数是一个执行频率比较高的函数 会将当前函数标记为 Hot Function 同时由 TurboFan 模块将当前函数变为 优化后的机器码(MachineCode) 当我们后续执行当前函数时根本就不需要再讲字节码转换为 CPU 指令集 直接执行机器指令的运算结果

  5. 但是 TurboFan 优化也是有问题的

    const sum = (a, b) => a + b;
    sum(1, 2);
    sum("foo", "bar");
    

    上面执行背后是不同的 cpu 指令集 (一个是相加 一个是字符串拼接) 这时候 V8 引擎就会做反向优化(De optimization) 把机器指令又转换为字节码 后续转化为 CPU 指令集 运行结果 所以从这个角度出发 ts 代码是要比 js 代码运行效率稍显高点的