javascript
js图片转二进制流_V8是如何执行一段JS代码的?
- 匯編器 編譯器 解釋器
- 解釋執行和解釋執行
- 什么是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?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.執行代碼及優化
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代码的?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GitHub访问慢-FastGithub
- 下一篇: python两个字典合并,两个list合