js图片转二进制流_V8是如何执行一段JS代码的?

  • 汇编器 编译器 解释器
  • 解释执行和解释执行
  • 什么是V8?
  • V8执行Js代码的过程

汇编器 编译器 解释器

众所周知,计算机只能理解机器语言,而我们平时编程用的通常是高级语言,所以源代码通常都要经过层层转换最终变成机器语言运行。

9011e14546fa533794726cbeb8076b4e.png

早期只有汇编语言没有高级语言,不同的设备有一套自己的对应不同机器语言指令集的汇编语言,也就是说,汇编语言不能在不同CPU架构(intel ARM MIPS)之间移植。同一个软件为了让不同类型的处理器架构都能兼容要写好几套代码,实在太不方便了,我们需要一种屏蔽了计算机架构细节的语言,能适应多种不同CPU架构的语言,专心处理业务逻辑,所以后来发展出了跨平台的高级语言。诸如 C C++ Java JavaScript。

bf11282bdaae3900a23ffa2633ba93c1.png
汇编时代
73aff3009ee1dc9c5bf80f754d9f2072.png
高级语言时代

随着计算机发展,编译器也越来越复杂,发展了很多分支,像是本地编译器、交叉编译器等,这里就不多说了。

那么源码一定要经过编译才能运行吗?

解释器的出现给出了一种不用编译就能运行的能力

651586c58fc5edda70f62355af370d7e.png
7673bef92290ad1e487757802b8ec58e.png

先静态编译到可执行的文件,然后运行可执行的文件来执行程序,而解释器提供了一种边编译边运行的动态运行方法,而也正因为通过解释器运行的代码是边编译边运行的,所以运行的速度比静态编译的那种慢很多。

所以程序运行的方式分为编译执行和解释执行。

解释执行和解释执行

解释执行:需要先将输入的源代码通过解析器编译成中间代码,之后直接使用解释器解释执行中间代码,然后直接输出结果。

d2147ae888cda122b319950c8f4a21f8.png

第二种是编译执行。采用这种方式时,也需要先将源代码转换为中间代码,然后我们的编译器再将中间代码编译成机器代码。通常编译成的机器代码是以二进制文件形式存储的,需要执行这段程序的时候直接执行二进制文件就可以了。还可以使用虚拟机将编译后的机器代码保存在内存中,然后直接执行内存中的二进制代码。

6e3c991c1687a3b83c94df15e086865b.png

什么是V8

JavaScript引擎是执行JavaScript代码的程序或解释器。javaScript引擎可以实现为标准解释器或即时编译器,它以某种形式将JavaScript编译为字节码。

那么除了v8引擎,你还知道那些js引擎

  • V8 - 开源,由Google开发,用C ++编写

  • Rhin- 由Mozilla基金会开源,完全用Java开发

  • SpiderMonkey 第一个JavaScript引擎,Netscape Navigator,Firefox

  • JavaScriptCore 苹果公司为Safari开发

  • KJS 最初由Harri Porten为KDE项目的Konqueror网络浏览器开发

  • Chakra** (JScript9) Microsoft Edge

  • Chakra** (JavaScript) Microsoft IE9-IE11

  • Nashorn 作为OpenJDK的一部分,由Oracle Java语言和工具组编写

  • JerryScript 一个物联网的轻量级引擎

V8 是用 C++ 写的,使用了即时编译技术,工作模式如下图

f1815dd21f3bbd271ad7e3c998e15251.png在这个过程中,V8同时使用了Parser(解析器)、Ignition(解释器) 和TurboFan(编译器) 来执行Js代码。V8是被设计用来提高网页浏览器内部JavaScript执行的性能,那么如何提高性能呢?

为了提高性能,v8会把js代码转换为高效的机器码,而不在是依赖于解释器去执行。v8引入了 JIT在运行时把js代码进行转换为机器码。这里的主要区别在于V8不生成字节码或任何中间代码。

v8曾经有两个编译器(v5.9之前)

full-codegen — 一个简单且速度非常快的编译器,可以生成简单且相对较慢的机器码 Crankshaft —  一个更复杂的(Just-In-Time)优化编译器,生成高度优化的代码

v8充分多进程,主进程负责获取代码,编译生成机器码,有专门负责优化的进程,,还有一个监控进程负责分析那些代码执行比较慢,以遍Crankshaft 做优化,最后还有一个就是GC进程,负责内存垃圾回收。

V8执行Js代码的过程

1 Parser生成抽象语法树

在Chrome中开始下载Javascript文件后,Parser就会开始并行在单独的线程上解析代码。这意味着解析可以在下载完成后仅几毫秒内完成,并生成AST。

0eaeb725439a7168985009eb26416f83.png
函数
6b5d92e5c9b5f85897c1241e38ebd926.png
编译AST后

AST还广泛应用于各类项目中,比如Babel、ESLint,以及我们面试常说手写loader,那么AST的生成过程是怎么样的呢?

我们首先看一下Babel的实现:

// 解析 
let esprima = require('esprima') 
// 转换 
let estraverse = require('estraverse') 
// 生成 
let escodegen = require('escodegen') 
 
let code = `function code () {}` 
 
// 解析 
let ast = esprima.parseScript(code) 
// 转换 
estraverse.traverse(ast, { 
  enter (node) { 
    console.log('enter' + node.type) 
    if (node.type === 'enterIdentifier') { 
      node.name = 'hello' 
    } 
  }, 
  leave (node) { 
    console.log('enter' + node.type) 
  } 
}) 
// 生成 
let newCode = escodegen.generate(ast)

我们常说的生成AST不管是Babel 还是vue中 都是 解析这一步:

解析步骤接收代码并输出 AST。这个步骤分为两个阶段:词法分析(Lexical Analysis)和 语法分析(Syntactic Analysis)。

词法分析:将代码(字符串)分割为token流,即语法单元成的数组 语法分析:分析token流(上面生成的数组)并生成 AST 转换

var name = "react"
//转成token后为

[
    {
        "type": "Keyword",
        "value": "var"
    },
    {
        "type": "Identifier",
        "value": "name"
    },
    {
        "type": "Punctuator",
        "value": "="
    },
    {
        "type": "String",
        "value": "\"react\""
    },
    {
        "type": "Punctuator",
        "value": ";"
    }
]

从上面可以看出,var name = "ivweb"; 这样一段代码,会有关键字"var"、标识符"name"、赋值运算符"="、字符串"ivweb"、分隔符";",共5个token。

2.Ignition生成字节码

解释器 Ignition 根据语法树生成字节码。TurboFan 是 V8 的优化编译器,TurboFan 将字节码生成优化的机器代码。

9a862bb952394f57875dd0d59aaa3474.png

如果想知道为什么会有两种执行模式?点击查看视频 [](Franziska Hinkelmann: JavaScript engines - how do they even? | JSConf EU 2017)

字节码是机器代码的抽象。如果字节码采用和物理 CPU 相同的计算模型进行设计,则将字节码编译为机器代码更容易。这就是为什么解释器(interpreter)常常是寄存器或堆栈。Ignition 是具有累加器的寄存器。

8339c497c9d5f9d53e9cab88a2ec69e6.png

您可以将 V8 的字节码看作是小型的构建块(bytecodes as small building blocks),这些构建块组合在一起构成任何 JavaScript 功能。V8 有数以百计的字节码。比如 Add 或 TypeOf 这样的操作符,或者像 LdaNamedProperty 这样的属性加载符,还有很多类似的字节码。V8还有一些非常特殊的字节码,如 CreateObjectLiteral 或 SuspendGenerator。头文件 bytecodes.h 定义了 V8 字节码的完整列表。

每个字节码指定其输入和输出作为寄存器操作数。Ignition 使用寄存器 r0,r1,r2,... 和累加器寄存器(accumulator register)。几乎所有的字节码都使用累加器寄存器。它像一个常规寄存器,除了字节码没有指定。例如,Add r1 将寄存器 r1 中的值和累加器中的值进行加法运算。这使得字节码更短,节省内存。

许多字节码以 Lda 或 Sta 开头。Lda 和 Stastands 中的 a 为累加器(accumulator)。例如,LdaSmi [42] 将小整数(Smi)42 加载到累加器寄存器中。Star r0 将当前在累加器中的值存储在寄存器 r0 中。

以现在掌握的基础知识,花点时间来看一个具有实际功能的字节码。

function incrementX(obj) {
  return 1 + obj.x;
}

incrementX({x: 42});
// V8 的编译器是惰性的,
// 如果一个函数没有运行,V8 将不会解释它

ps 如果要查看 V8 的 JavaScript 字节码,可以使用在命令行参数中添加 --print-bytecode 运行 D8 或Node.js(8.3 或更高版本)来打印。对于 Chrome,请从命令行启动 Chrome,使用 --js-flags="--print-bytecode",请参考 Run Chromium with flags。

$ node --print-bytecode incrementX.js
...
[generating bytecode for function: incrementX]
Parameter count 2
Frame size 8
  12 E> 0x2ddf8802cf6e @    StackCheck
  19 S> 0x2ddf8802cf6f @    LdaSmi [1]
        0x2ddf8802cf71 @    Star r0
  34 E> 0x2ddf8802cf73 @    LdaNamedProperty a0, [0], [4]
  28 E> 0x2ddf8802cf77 @    Add r0, [6]
  36 S> 0x2ddf8802cf7a @    Return
Constant pool (size = 1)
0x2ddf8802cf21: [FixedArray] in OldSpace
 - map = 0x2ddfb2d02309 
 - length: 1
           0: 0x2ddf8db91611 
Handler Table (size = 16)
3.执行代码及优化
441854d2d8746eb28dfb121878f09a13.png

Ignition执行上一步生成的字节码,并记录代码运行的次数等信息,如果同一段代码执行了很多次,就会被标记为 “HotSpot”(热点代码),然后把这段代码发送给 编译器TurboFan,然后TurboFan把它编译为更高效的机器码储存起来,等到下次再执行到这段代码时,就会用现在的机器码替换原来的字节码进行执行,这样大大提升了代码的执行效率。另外,当TurboFan判断一段代码不再为热点代码的时候,会执行去优化的过程,把优化的机器码丢掉,然后执行过程回到Ignition。

其实字节码配合解释器和编译器是很火的一种技术,例如 Python 和 Java 的虚拟机也是基于这种技术,我们把这种技术称为即时编译(JIT)。

这么多语言和工作引擎都使用了“字节码 + JIT”技术,其具体工作流程如下:

eefc924325da592ea988bc9037ce9ecb.png

推荐阅读

(点击标题可跳转阅读)

RxJS入门

一文掌握Webpack编译流程

一文深度剖析Axios源码

Javascript条件逻辑设计重构Promise知识点自测你不知道的React Diff你不知道的GIT神操作程序中代码坏味道(上)

程序中代码坏味道(下)

学习Less,看这篇就够了

一文掌握GO语言实战技能(一)

一文掌握GO语言实战技能(二)

Linux-

Linux-

一文达到Mysql实战水平

一文达到Mysql实战水平-习题答案

从表单抽象到表单中台

vue(1)- new Vue

实战LeetCode 系列(一) (题目+解析)

一文掌握Javascript函数式编程重点

实战LeetCode - 前端面试必备二叉树算法

 React16.0-16.6 ( )

阿里、网易、滴滴、今日头条、有赞.....等20家面试真题

30分钟学会 snabbdom 源码,实现精简的 Virtual DOM 库

觉得本文对你有帮助?请分享给更多人

关注「React中文社区」加星标,每天进步

84d5c8c2343fd2ad0de34ae404f3961c.png

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值