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

汇编器 编译器 解释器

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


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

汇编时代

高级语言时代

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

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

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



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

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

解释执行和解释执行

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


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


什么是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++ 写的,使用了即时编译技术,工作模式如下图

在这个过程中,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。

函数

编译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 将字节码生成优化的机器代码。


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

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


您可以将 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 2Frame 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 @    ReturnConstant pool (size = 1)0x2ddf8802cf21: [FixedArray] in OldSpace - map = 0x2ddfb2d02309  - length: 1           0: 0x2ddf8db91611 Handler Table (size = 16)

3.执行代码及优化


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

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

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


推荐阅读

(点击标题可跳转阅读)

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中文社区」加星标,每天进步

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

  1. js图片转二进制流_为了安全问题后台返回前端得文件为二进制得流,我们该如何处理这些流文件...

    1遇到问题得场景:本来后台返给前端得图片,音频,视频等文件前端直接就能访问到!可是为了安全前端要在次请求这个路径而且还要加进去一个请求头.请求成功后后台返回你二进制得流!然后前端在解析这个二进制得流才 ...

  2. c# 如何在webbrowser控件执行一段JS代码

    var doc = this.WebBrowser.Document.DomDocument as IHTMLDocument2; var win = doc.parentWindow as IHTM ...

  3. 如何执行一段java代码_V8 之 如何执行一段 JavaSscript 代码

    如何执行一段 JavaSscript 代码 解释执行 与 编译执行 解释执行,需要先将输入的源代码通过解析器编译成中间代码,之后直接使用解释器解释执行中间代码,然后直接输出结果. 编译执行.采用这种方 ...

  4. 用Visual Studio查看图片的二进制流

    有时候我们需要查看图片的二进制流,比如做iOS马甲包的时候,为了让图片的md5发生变化,又要保持图片本身不变,我们会在后面写入空行之类的操作(或者写0).怎么以二进制流的方式查看一张图片呢? 打开vi ...

  5. c#图片与二进制流相互转换

    个人认为这个比较有意思,可以把图片转换成二进制流存到数据库中,再转换出来,用来存储自己的私密照片. 两个函数代码如下: #region//图片转换为二进制流public void PictureToB ...

  6. springmvc使用谷歌captcha生成图片验证码,并将验证码图片以二进制流的方式返回给前端(app和pc端都能调用)

    近期对登录注册与获取短信验证码的接口做了安全限制,其中一部分就用到了谷歌的captcha验证码,比如当用户连续三次登陆失败,那么之后的登录请求就需要用户输入谷歌的图形验证码.由于web端和app端调用 ...

  7. python图片转为二进制流_python将图片转为二进制-女性时尚流行美容健康娱乐mv-ida网...

    女性时尚流行美容健康娱乐mv-ida网 mvida时尚娱乐网 首页 美容 护肤 化妆技巧 发型 服饰 健康 情感 美体 美食 娱乐 明星八卦 首页  > 高级搜索 mysql 5.7.9 lin ...

  8. Unity C#图片转换二进制流、精灵与Png、jpg互转

    Chinar blog :www.chinar.xin 图片转二进制流转换图片互转 本文提供全流程,中文翻译. Chinar 的初衷是将一种简单的生活方式带给世人 使有限时间 具备无限可能 China ...

  9. android图片转换为二进制流,Android图片二进制与Bitmap、Drawable之间的转换

    Android图片二进制与Bitmap.Drawable之间的转换 Java代码 public byte[] getBitmapByte(Bitmap bitmap){ ByteArrayOutput ...

最新文章

  1. 公路病害检测有了“智慧眼”,思谋AI“助力”广东省高速公路
  2. C++引入抽象基类和纯虚函数的作用和目的
  3. linux是32还是64位,如何看linux是32位还是64位
  4. wxwidget编译安装_wxWidgets的安装编译、相关配置、问题分析处理
  5. 那就是我mdash;mdash;大名鼎鼎的C++缪博士
  6. PCL学习(三) SAC-IA 估记object pose
  7. 组装台式电脑配置清单_萌新攒机必备!多价位台式电脑配置清单!
  8. vs2008下的javascript语法检查工具——JSLint.VS
  9. easyUI表单验证扩展
  10. 用openssh下的sftp通过chroot控制用户
  11. 变频器22b系列说明书_变频器接电位器正确接法
  12. java切割文件出现1k_java实现把一个大文件切割成N个固定大小的文件
  13. 几个LabVIEW软件欢迎页面
  14. 关于如何去实现百度的自动发帖功能猜想
  15. 《德鲁克管理思想精要》读书笔记
  16. 使用python开发多人一对一聊天系统(文字聊天,视频通讯,文件传输)
  17. 插件77:获取Yahoo!股票新闻
  18. USB Network Native Driver for ESXi更新到支持ESXi7.0.1
  19. 少儿编程 | 探讨C++课程、MIT Scratch课程、python课程、Noi竞赛、蓝桥怎么引导?如何才能让小孩子飞的更高?附开发工具的下载与安装
  20. 【arcgis天地图wmts加载显示空白】

热门文章

  1. Spring Boot配置MinIO(实现文件上传、下载、删除)
  2. js 二叉树图形_js数据结构和算法(三)二叉树
  3. Spring Boot整合Redis——自定义RedisSerializer
  4. 详解HTTP协议~~~
  5. mysql三表where查询_mysql三表查询sql语句
  6. zabbix mysql脚本_zabbix监控mysql脚本
  7. 单片机实验:节日彩灯控制器
  8. qgis折点打断_arcgis在折点处打断并建立网络分析(最短路径等问题)
  9. java8安装_科学网—Java JDK 8 的安装以及环境变量的配置(Linux and Windows) - 彭勇的博文...
  10. apache poi excel显示 base64 图片_数据处理之带图片Excel数据处理解惑