av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

瀏覽器是如何工作的:Chrome V8讓你更懂JavaScript

V8 是由 Google 開發(fā)的開源 JavaScript 引擎,也被稱為虛擬機(jī),模擬實(shí)際計(jì)算機(jī)各種功能來實(shí)現(xiàn)代碼的編譯和執(zhí)行。

記得那年花下,深夜,初識(shí)謝娘時(shí)

為什么需要 JavaScript 引擎

我們寫的 JavaScript 代碼直接交給瀏覽器或者 Node 執(zhí)行時(shí),底層的 CPU 是不認(rèn)識(shí)的,也沒法執(zhí)行。CPU 只認(rèn)識(shí)自己的指令集,指令集對(duì)應(yīng)的是匯編代碼。寫匯編代碼是一件很痛苦的事情。并且不同類型的 CPU 的指令集是不一樣的,那就意味著需要給每一種 CPU 重寫匯編代碼。

JavaScirpt 引擎可以將 JS 代碼編譯為不同 CPU(Intel, ARM 以及 MIPS 等)對(duì)應(yīng)的匯編代碼,這樣我們就不需要去翻閱每個(gè) CPU 的指令集手冊(cè)來編寫匯編代碼了。當(dāng)然,JavaScript 引擎的工作也不只是編譯代碼,它還要負(fù)責(zé)執(zhí)行代碼、分配內(nèi)存以及垃圾回收。

 
 
 
 
  1. 1000100111011000  #機(jī)器指令  
  2. mov ax,bx         #匯編指令 

資料拓展: 匯編語言入門教程【阮一峰】 | 理解 V8 的字節(jié)碼「譯」

https://zhuanlan.zhihu.com/p/28590489

熱門 JavaScript 引擎

  •  V8 (Google),用 C++編寫,開放源代碼,由 Google 丹麥開發(fā),是 Google Chrome 的一部分,也用于 Node.js。
  •  JavaScriptCore (Apple),開放源代碼,用于 webkit 型瀏覽器,如 Safari ,2008 年實(shí)現(xiàn)了編譯器和字節(jié)碼解釋器,升級(jí)為了 SquirrelFish。蘋果內(nèi)部代號(hào)為“Nitro”的 JavaScript 引擎也是基于 JavaScriptCore 引擎的。
  •  Rhino,由 Mozilla 基金會(huì)管理,開放源代碼,完全以 Java 編寫,用于 HTMLUnit
  •  SpiderMonkey (Mozilla),第一款 JavaScript 引擎,早期用于 Netscape Navigator,現(xiàn)時(shí)用于 Mozilla Firefox。
  •  Chakra (JScript 引擎),用于 Internet Explorer。
  •  Chakra (JavaScript 引擎),用于 Microsoft Edge。
  •  KJS,KDE 的 ECMAScript/JavaScript 引擎,最初由哈里·波頓開發(fā),用于 KDE 項(xiàng)目的 Konqueror 網(wǎng)頁瀏覽器中。
  •  JerryScript — 三星推出的適用于嵌入式設(shè)備的小型 JavaScript 引擎。
  •  其他:Nashorn、QuickJS 、 Hermes

V8

Google V8 引擎是用 C ++編寫的開源高性能 JavaScript 和 WebAssembly 引擎,它已被用于 Chrome 和 Node.js 等??梢赃\(yùn)行在 Windows 7+,macOS 10.12+和使用 x64,IA-32,ARM 或 MIPS 處理器的 Linux 系統(tǒng)上。V8 最早被開發(fā)用以嵌入到 Google 的開源瀏覽器 Chrome 中,第一個(gè)版本隨著第一版Chrome于 2008 年 9 月 2 日發(fā)布。但是 V8 是一個(gè)可以獨(dú)立運(yùn)行的模塊,完全可以嵌入到任何 C ++應(yīng)用程序中。著名的 Node.js( 一個(gè)異步的服務(wù)器框架,可以在服務(wù)端使用 JavaScript 寫出高效的網(wǎng)絡(luò)服務(wù)器 ) 就是基于 V8 引擎的,Couchbase, MongoDB 也使用了 V8 引擎。??

和其他 JavaScript 引擎一樣,V8 會(huì)編譯 / 執(zhí)行 JavaScript 代碼,管理內(nèi)存,負(fù)責(zé)垃圾回收,與宿主語言的交互等。通過暴露宿主對(duì)象 ( 變量,函數(shù)等 ) 到 JavaScript,JavaScript 可以訪問宿主環(huán)境中的對(duì)象,并在腳本中完成對(duì)宿主對(duì)象的操作。

與君初相識(shí),猶如故人歸

什么是 D8

d8 是一個(gè)非常有用的調(diào)試工具,你可以把它看成是 debug for V8 的縮寫。我們可以使用 d8 來查看 V8 在執(zhí)行 JavaScript 過程中的各種中間數(shù)據(jù),比如作用域、AST、字節(jié)碼、優(yōu)化的二進(jìn)制代碼、垃圾回收的狀態(tài),還可以使用 d8 提供的私有 API 查看一些內(nèi)部信息。

安裝 D8

  •  方法一:自行下載編譯
    •  v8 google 下載及編譯使用
    •  官方文檔:Using d8
  •  方法二:使用編譯好的 d8 工具
  •  mac 平臺(tái):

https://storage.googleapis.com/chromium-v8/official/canary/v8-mac64-dbg-8.4.109.zip

  •  linux32 平臺(tái):

https://storage.googleapis.com/chromium-v8/official/canary/v8-linux32-dbg-8.4.109.zip

  •  linux64 平臺(tái):

https://storage.googleapis.com/chromium-v8/official/canary/v8-linux64-dbg-8.4.109.zip

  • win32 平臺(tái):

https://storage.googleapis.com/chromium-v8/official/canary/v8-win32-dbg-8.4.109.zip

  • win64 平臺(tái):

https://storage.googleapis.com/chromium-v8/official/canary/v8-win64-dbg-8.4.109.zip

 
 
 
 
  1. // 解壓文件,點(diǎn)擊d8打開(mac安全策略限制的話,按住control,再點(diǎn)擊,彈出菜單中選擇打開)  
  2.   V8 version 8.4.109  
  3.   d8> 1 + 2  
  4.     3  
  5.   d8> 2 + '4'  
  6.     "24"  
  7.   d8> console.log(23)  
  8.     23  
  9.     undefined  
  10.   d8> var a = 1  
  11.     undefined  
  12.   d8> a + 2  
  13.     3  
  14.   d8> this  
  15.     [object global]  
  16.   d8> 

本文后續(xù)用于 demo 演示時(shí)的文件目錄結(jié)構(gòu):

 
 
 
 
  1. V8:  
  2.    # d8可執(zhí)行文件  
  3.    d8  
  4.    icudtl.dat  
  5.    libc++.dylib  
  6.    libchrome_zlib.dylib  
  7.    libicui18n.dylib  
  8.    libicuuc.dylib  
  9.    libv8.dylib  
  10.    libv8_debug_helper.dylib  
  11.    libv8_for_testing.dylib  
  12.    libv8_libbase.dylib  
  13.    libv8_libplatform.dylib  
  14.    obj  
  15.    snapshot_blob.bin  
  16.    v8_build_config.json  
  17.    # 新建的js示例文件  
  18.    test.js 
  •  方法三:mac 
 
 
 
 
  1. # 如果已有HomeBrew,忽略第一條命令  
  2.   ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"  
  3.   brew install v8 
  •  方法四:使用 node 代替,比如可以用node --print-bytecode ./test.js,打印出 Ignition(解釋器)生成的 Bytecode(字節(jié)碼)。

都有哪些 d8 命令可供使用?

  •  查看 d8 命令 
 
 
 
 
  1. # 如果不想使用./d8這種方式進(jìn)行調(diào)試,可將d8加入環(huán)境變量,之后就可以直接`d8 --help`了  
  2.   ./d8 --help 
  •  過濾特定的命令,如: 
 
 
 
 
  1. # 如果是 Windows 系統(tǒng),可能缺少 grep 程序,請(qǐng)自行下載安裝并添加環(huán)境變量  
  2.   ./d8 --help |grep print 
  •   print-bytecode 查看生成的字節(jié)碼
  •   print-opt-code 查看優(yōu)化后的代碼
  •   print-ast 查看中間生成的 AST
  •   print-scopes 查看中間生成的作用域
  •   trace-gc 查看這段代碼的內(nèi)存回收狀態(tài)
  •   trace-opt 查看哪些代碼被優(yōu)化了
  •   trace-deopt 查看哪些代碼被反優(yōu)化了
  •   turbofan-stats 打印優(yōu)化編譯器的一些統(tǒng)計(jì)數(shù)據(jù)

使用 d8 進(jìn)行調(diào)試

 
 
 
 
  1. // test.js  
  2. function sum(a) {  
  3.   var b = 6;  
  4.   return a + 6;  
  5. }  
  6. console.log(sum(3));  
 
 
 
 
  1. # d8 后面跟上文件名和要執(zhí)行的命令,如執(zhí)行下面這行命令,就會(huì)打印出 test.js 文件所生成的字節(jié)碼。  
  2.   ./d8 ./test.js --print-bytecode  
  3.   # 執(zhí)行以下命令,輸出9  
  4.   ./d8 ./test.js 

內(nèi)部方法

你還可以使用 V8 所提供的一些內(nèi)部方法,只需要在啟動(dòng) V8 時(shí)傳入 --allow-natives-syntax 命令,你就可以在 test.js 中使用諸如HasFastProperties(檢查一個(gè)對(duì)象是否擁有快屬性)的內(nèi)部方法(索引屬性、常規(guī)屬性、快屬性等下文會(huì)介紹)。

 
 
 
 
  1. function Foo(property_num, element_num) {  
  2.   //添加可索引屬性  
  3.   for (let i = 0; i < element_num; i++) {  
  4.     this[i] = `element${i}`;  
  5.   }  
  6.   //添加常規(guī)屬性  
  7.   for (let i = 0; i < property_num; i++) {  
  8.     let ppt = `property${i}`;  
  9.     this[ppt] = ppt;  
  10.   }  
  11. }  
  12. var bar = new Foo(10, 10);  
  13. // 檢查一個(gè)對(duì)象是否擁有快屬性  
  14. console.log(%HasFastProperties(bar));  
  15. delete bar.property2;  
  16. console.log(%HasFastProperties(bar));  
 
 
 
 
  1. ./d8 --allow-natives-syntax ./test.js  
  2. # 依次打?。簍rue false 

心似雙絲網(wǎng),中有千千結(jié)

V8 引擎的內(nèi)部結(jié)構(gòu)

V8 是一個(gè)非常復(fù)雜的項(xiàng)目,有超過 100 萬行 C++代碼。它由許多子模塊構(gòu)成,其中這 4 個(gè)模塊是最重要的:

  •  Parser:負(fù)責(zé)將 JavaScript 源碼轉(zhuǎn)換為 Abstract Syntax Tree (AST)
  •  Ignition:interpreter,即解釋器,負(fù)責(zé)將 AST 轉(zhuǎn)換為 Bytecode,解釋執(zhí)行 Bytecode;同時(shí)收集 TurboFan 優(yōu)化編譯所需的信息,比如函數(shù)參數(shù)的類型;解釋器執(zhí)行時(shí)主要有四個(gè)模塊,內(nèi)存中的字節(jié)碼、寄存器、棧、堆。

通常有兩種類型的解釋器,基于棧 (Stack-based)和基于寄存器 (Register-based),基于棧的解釋器使用棧來保存函數(shù)參數(shù)、中間運(yùn)算結(jié)果、變量等;基于寄存器的虛擬機(jī)則支持寄存器的指令操作,使用寄存器來保存參數(shù)、中間計(jì)算結(jié)果。通常,基于棧的虛擬機(jī)也定義了少量的寄存器,基于寄存器的虛擬機(jī)也有堆棧,其區(qū)別體現(xiàn)在它們提供的指令集體系。大多數(shù)解釋器都是基于棧的,比如 Java 虛擬機(jī),.Net 虛擬機(jī),還有早期的 V8 虛擬機(jī)?;诙褩5奶摂M機(jī)在處理函數(shù)調(diào)用、解決遞歸問題和切換上下文時(shí)簡(jiǎn)單明快。而現(xiàn)在的 V8 虛擬機(jī)則采用了基于寄存器的設(shè)計(jì),它將一些中間數(shù)據(jù)保存到寄存器中。

基于寄存器的解釋器架構(gòu): 

  •  TurboFan:compiler,即編譯器,利用 Ignitio 所收集的類型信息,將 Bytecode 轉(zhuǎn)換為優(yōu)化的匯編代碼;
  •  Orinoco:garbage collector,垃圾回收模塊,負(fù)責(zé)將程序不再需要的內(nèi)存空間回收。

其中,Parser,Ignition 以及 TurboFan 可以將 JS 源碼編譯為匯編代碼,其流程圖如下:

??

簡(jiǎn)單地說,Parser 將 JS 源碼轉(zhuǎn)換為 AST,然后 Ignition 將 AST 轉(zhuǎn)換為 Bytecode,最后 TurboFan 將 Bytecode 轉(zhuǎn)換為經(jīng)過優(yōu)化的 Machine Code(實(shí)際上是匯編代碼)。

  •  如果函數(shù)沒有被調(diào)用,則 V8 不會(huì)去編譯它。
  •  如果函數(shù)只被調(diào)用 1 次,則 Ignition 將其編譯 Bytecode 就直接解釋執(zhí)行了。TurboFan 不會(huì)進(jìn)行優(yōu)化編譯,因?yàn)樗枰?Ignition 收集函數(shù)執(zhí)行時(shí)的類型信息。這就要求函數(shù)至少需要執(zhí)行 1 次,TurboFan 才有可能進(jìn)行優(yōu)化編譯。
  •  如果函數(shù)被調(diào)用多次,則它有可能會(huì)被識(shí)別為熱點(diǎn)函數(shù),且 Ignition 收集的類型信息證明可以進(jìn)行優(yōu)化編譯的話,這時(shí) TurboFan 則會(huì)將 Bytecode 編譯為 Optimized Machine Code(已優(yōu)化的機(jī)器碼),以提高代碼的執(zhí)行性能。??

圖片中的紅色虛線是逆向的,也就是說Optimized Machine Code 會(huì)被還原為 Bytecode,這個(gè)過程叫做 Deoptimization。這是因?yàn)?Ignition 收集的信息可能是錯(cuò)誤的,比如 add 函數(shù)的參數(shù)之前是整數(shù),后來又變成了字符串。生成的 Optimized Machine Code 已經(jīng)假定 add 函數(shù)的參數(shù)是整數(shù),那當(dāng)然是錯(cuò)誤的,于是需要進(jìn)行 Deoptimization。

 
 
 
 
  1. function add(x, y) {  
  2.   return x + y;  
  3. }  
  4. add(3, 5);  
  5. add('3', '5'); 

在運(yùn)行 C、C++以及 Java 等程序之前,需要進(jìn)行編譯,不能直接執(zhí)行源碼;但對(duì)于 JavaScript 來說,我們可以直接執(zhí)行源碼(比如:node test.js),它是在運(yùn)行的時(shí)候先編譯再執(zhí)行,這種方式被稱為即時(shí)編譯(Just-in-time compilation),簡(jiǎn)稱為 JIT。因此,V8 也屬于 JIT 編譯器。

V8 是怎么執(zhí)行一段 JavaScript 代碼的

  •  在 V8 出現(xiàn)之前,所有的 JavaScript 虛擬機(jī)所采用的都是解釋執(zhí)行的方式,這是 JavaScript 執(zhí)行速度過慢的一個(gè)主要原因。而 V8 率先引入了即時(shí)編譯(JIT)的雙輪驅(qū)動(dòng)的設(shè)計(jì)(混合使用編譯器和解釋器的技術(shù)),這是一種權(quán)衡策略,混合編譯執(zhí)行和解釋執(zhí)行這兩種手段,給 JavaScript 的執(zhí)行速度帶來了極大的提升。V8 出現(xiàn)之后,各大廠商也都在自己的 JavaScript 虛擬機(jī)中引入了 JIT 機(jī)制,所以目前市面上 JavaScript 虛擬機(jī)都有著類似的架構(gòu)。另外,V8 也是早于其他虛擬機(jī)引入了惰性編譯、內(nèi)聯(lián)緩存、隱藏類等機(jī)制,進(jìn)一步優(yōu)化了 JavaScript 代碼的編譯執(zhí)行效率。
  •  V8 執(zhí)行一段 JavaScript 的流程圖:    

  •  V8 本質(zhì)上是一個(gè)虛擬機(jī),因?yàn)橛?jì)算機(jī)只能識(shí)別二進(jìn)制指令,所以要讓計(jì)算機(jī)執(zhí)行一段高級(jí)語言通常有兩種手段:
    •  第一種是將高級(jí)代碼轉(zhuǎn)換為二進(jìn)制代碼,再讓計(jì)算機(jī)去執(zhí)行;
    •  另外一種方式是在計(jì)算機(jī)安裝一個(gè)解釋器,并由解釋器來解釋執(zhí)行。
  •  解釋執(zhí)行和編譯執(zhí)行都有各自的優(yōu)缺點(diǎn),解釋執(zhí)行啟動(dòng)速度快,但是執(zhí)行時(shí)速度慢,而編譯執(zhí)行啟動(dòng)速度慢,但是執(zhí)行速度快。為了充分地利用解釋執(zhí)行和編譯執(zhí)行的優(yōu)點(diǎn),規(guī)避其缺點(diǎn),V8 采用了一種權(quán)衡策略,在啟動(dòng)過程中采用了解釋執(zhí)行的策略,但是如果某段代碼的執(zhí)行頻率超過一個(gè)值,那么 V8 就會(huì)采用優(yōu)化編譯器將其編譯成執(zhí)行效率更加高效的機(jī)器代碼。
  •  總結(jié):

V8 執(zhí)行一段 JavaScript 代碼所經(jīng)歷的主要流程包括:

  • 初始化基礎(chǔ)環(huán)境;
  • 解析源碼生成 AST 和作用域;
  • 依據(jù) AST 和作用域生成字節(jié)碼;
  • 解釋執(zhí)行字節(jié)碼;
  • 監(jiān)聽熱點(diǎn)代碼;
  • 優(yōu)化熱點(diǎn)代碼為二進(jìn)制的機(jī)器代碼;
  • 反優(yōu)化生成的二進(jìn)制機(jī)器代碼。

一等公民與閉包

一等公民的定義

  •  在編程語言中,一等公民可以作為函數(shù)參數(shù),可以作為函數(shù)返回值,也可以賦值給變量。
  •  如果某個(gè)編程語言的函數(shù),可以和這個(gè)語言的數(shù)據(jù)類型做一樣的事情,我們就把這個(gè)語言中的函數(shù)稱為一等公民。例如,字符串在幾乎所有編程語言中都是一等公民,字符串可以做為函數(shù)參數(shù),字符串可以作為函數(shù)返回值,字符串也可以賦值給變量。對(duì)于各種編程語言來說,函數(shù)就不一定是一等公民了,比如 Java 8 之前的版本。
  •  對(duì)于 JavaScript 來說,函數(shù)可以賦值給變量,也可以作為函數(shù)參數(shù),還可以作為函數(shù)返回值,因此 JavaScript 中函數(shù)是一等公民。

動(dòng)態(tài)作用域與靜態(tài)作用域

  •  如果一門語言的作用域是靜態(tài)作用域,那么符號(hào)之間的引用關(guān)系能夠根據(jù)程序代碼在編譯時(shí)就確定清楚,在運(yùn)行時(shí)不會(huì)變。某個(gè)函數(shù)是在哪聲明的,就具有它所在位置的作用域。它能夠訪問哪些變量,那么就跟這些變量綁定了,在運(yùn)行時(shí)就一直能訪問這些變量。即靜態(tài)作用域可以由程序代碼決定,在編譯時(shí)就能完全確定。大多數(shù)語言都是靜態(tài)作用域的。
  •  動(dòng)態(tài)作用域(Dynamic Scope)。也就是說,變量引用跟變量聲明不是在編譯時(shí)就綁定死了的。在運(yùn)行時(shí),它是在運(yùn)行環(huán)境中動(dòng)態(tài)地找一個(gè)相同名稱的變量。在 macOS 或 Linux 中用的 bash 腳本語言,就是動(dòng)態(tài)作用域的。

閉包的三個(gè)基礎(chǔ)特性

  •  JavaScript 語言允許在函數(shù)內(nèi)部定義新的函數(shù)
  •  可以在內(nèi)部函數(shù)中訪問父函數(shù)中定義的變量
  •  因?yàn)?JavaScript 中的函數(shù)是一等公民,所以函數(shù)可以作為另外一個(gè)函數(shù)的返回值 
 
 
 
 
  1. // 閉包(靜態(tài)作用域,一等公民,調(diào)用棧的矛盾體) 
  2.  
  3. function foo() {  
  4.   var d = 20;  
  5.   return function inner(a, b) {  
  6.     const c = a + b + d;  
  7.     return c;  
  8.   };  
  9. }  
  10. const f = foo(); 

關(guān)于閉包,可參考我以前的一篇文章,在此不再贅述,在此主要談下閉包給 Chrome V8 帶來的問題及其解決策略。

惰性解析??

所謂惰性解析是指解析器在解析的過程中,如果遇到函數(shù)聲明,那么會(huì)跳過函數(shù)內(nèi)部的代碼,并不會(huì)為其生成 AST 和字節(jié)碼,而僅僅生成頂層代碼的 AST 和字節(jié)碼。

  • 在編譯 JavaScript 代碼的過程中,V8 并不會(huì)一次性將所有的 JavaScript 解析為中間代碼,這主要是基于以下兩點(diǎn):
    •  首先,如果一次解析和編譯所有的 JavaScript 代碼,過多的代碼會(huì)增加編譯時(shí)間,這會(huì)嚴(yán)重影響到首次執(zhí)行 JavaScript 代碼的速度,讓用戶感覺到卡頓。因?yàn)橛袝r(shí)候一個(gè)頁面的 JavaScript 代碼很大,如果要將所有的代碼一次性解析編譯完成,那么會(huì)大大增加用戶的等待時(shí)間;
    • 其次,解析完成的字節(jié)碼和編譯之后的機(jī)器代碼都會(huì)存放在內(nèi)存中,如果一次性解析和編譯所有 JavaScript 代碼,那么這些中間代碼和機(jī)器代碼將會(huì)一直占用內(nèi)存。
  • 基于以上的原因,所有主流的 JavaScript 虛擬機(jī)都實(shí)現(xiàn)了惰性解析。
  • 閉包給惰性解析帶來的問題:上文的 d 不能隨著 foo 函數(shù)的執(zhí)行上下文被銷毀掉。

預(yù)解析器

V8 引入預(yù)解析器,比如當(dāng)解析頂層代碼的時(shí)候,遇到了一個(gè)函數(shù),那么預(yù)解析器并不會(huì)直接跳過該函數(shù),而是對(duì)該函數(shù)做一次快速的預(yù)解析。

  •  判斷當(dāng)前函數(shù)是不是存在一些語法上的錯(cuò)誤,發(fā)現(xiàn)了語法錯(cuò)誤,那么就會(huì)向 V8 拋出語法錯(cuò)誤;
  •  檢查函數(shù)內(nèi)部是否引用了外部變量,如果引用了外部的變量,預(yù)解析器會(huì)將棧中的變量復(fù)制到堆中,在下次執(zhí)行到該函數(shù)的時(shí)候,直接使用堆中的引用,這樣就解決了閉包所帶來的問題。

V8 內(nèi)部是如何存儲(chǔ)對(duì)象的:快屬性和慢屬性

下面的代碼會(huì)輸出什么:

 
 
 
 
  1. // test.js  
  2. function Foo() {  
  3.   this[200] = 'test-200';  
  4.   this[1] = 'test-1';  
  5.   this[100] = 'test-100';  
  6.   this['B'] = 'bar-B';  
  7.   this[50] = 'test-50';  
  8.   this[9] = 'test-9';  
  9.   this[8] = 'test-8';  
  10.   this[3] = 'test-3';  
  11.   this[5] = 'test-5';  
  12.   this['D'] = 'bar-D';  
  13.   this['C'] = 'bar-C';  
  14. }  
  15. var bar = new Foo();   
  16. for (key in bar) {  
  17.   console.log(`index:${key}  value:${bar[key]}`);  
  18. }  
  19. //輸出:  
  20. // index:1  value:test-1  
  21. // index:3  value:test-3  
  22. // index:5  value:test-5  
  23. // index:8  value:test-8  
  24. // index:9  value:test-9  
  25. // index:50  value:test-50  
  26. // index:100  value:test-100  
  27. // index:200  value:test-200  
  28. // index:B  value:bar-B  
  29. // index:D  value:bar-D  
  30. // index:C  value:bar-C 

在ECMAScript 規(guī)范中定義了數(shù)字屬性應(yīng)該按照索引值大小升序排列,字符串屬性根據(jù)創(chuàng)建時(shí)的順序升序排列。在這里我們把對(duì)象中的數(shù)字屬性稱為排序?qū)傩?,?V8 中被稱為 elements,字符串屬性就被稱為常規(guī)屬性,在 V8 中被稱為 properties。在 V8 內(nèi)部,為了有效地提升存儲(chǔ)和訪問這兩種屬性的性能,分別使用了兩個(gè)線性數(shù)據(jù)結(jié)構(gòu)來分別保存排序?qū)傩院统R?guī)屬性。同時(shí) v8 將部分常規(guī)屬性直接存儲(chǔ)到對(duì)象本身,我們把這稱為對(duì)象內(nèi)屬性 (in-object properties),不過對(duì)象內(nèi)屬性的數(shù)量是固定的,默認(rèn)是 10 個(gè)。

 
 
 
 
  1. function Foo(property_num, element_num) {  
  2.   //添加可索引屬性  
  3.   for (let i = 0; i < element_num; i++) {  
  4.     this[i] = `element${i}`;  
  5.   }  
  6.   //添加常規(guī)屬性  
  7.   for (let i = 0; i < property_num; i++) {  
  8.     let ppt = `property${i}`;  
  9.     this[ppt] = ppt;  
  10.   }  
  11. }  
  12. var bar = new Foo(10, 10); 

可以通過 Chrome 開發(fā)者工具的 Memory 標(biāo)簽,捕獲查看當(dāng)前的內(nèi)存快照。通過增大第一個(gè)參數(shù)來查看存儲(chǔ)變化。

我們將保存在線性數(shù)據(jù)結(jié)構(gòu)中的屬性稱之為“快屬性”,因?yàn)榫€性數(shù)據(jù)結(jié)構(gòu)中只需要通過索引即可以訪問到屬性,雖然訪問線性結(jié)構(gòu)的速度快,但是如果從線性結(jié)構(gòu)中添加或者刪除大量的屬性時(shí),則執(zhí)行效率會(huì)非常低,這主要因?yàn)闀?huì)產(chǎn)生大量時(shí)間和內(nèi)存開銷。因此,如果一個(gè)對(duì)象的屬性過多時(shí),V8 就會(huì)采取另外一種存儲(chǔ)策略,那就是“慢屬性”策略,但慢屬性的對(duì)象內(nèi)部會(huì)有獨(dú)立的非線性數(shù)據(jù)結(jié)構(gòu) (字典) 作為屬性存儲(chǔ)容器。所有的屬性元信息不再是線性存儲(chǔ)的,而是直接保存在屬性字典中。

v8 屬性存儲(chǔ):

總結(jié):??

因?yàn)?JavaScript 中的對(duì)象是由一組組屬性和值組成的,所以最簡(jiǎn)單的方式是使用一個(gè)字典來保存屬性和值,但是由于字典是非線性結(jié)構(gòu),所以如果使用字典,讀取效率會(huì)大大降低。為了提升查找效率,V8 在對(duì)象中添加了兩個(gè)隱藏屬性,排序?qū)傩院统R?guī)屬性,element 屬性指向了 elements 對(duì)象,在 elements 對(duì)象中,會(huì)按照順序存放排序?qū)傩浴roperties 屬性則指向了 properties 對(duì)象,在 properties 對(duì)象中,會(huì)按照創(chuàng)建時(shí)的順序保存常規(guī)屬性。??

通過引入這兩個(gè)屬性,加速了 V8 查找屬性的速度,為了更加進(jìn)一步提升查找效率,V8 還實(shí)現(xiàn)了內(nèi)置內(nèi)屬性的策略,當(dāng)常規(guī)屬性少于一定數(shù)量時(shí),V8 就會(huì)將這些常規(guī)屬性直接寫進(jìn)對(duì)象中,這樣又節(jié)省了一個(gè)中間步驟。??

但是如果對(duì)象中的屬性過多時(shí),或者存在反復(fù)添加或者刪除屬性的操作,那么 V8 就會(huì)將線性的存儲(chǔ)模式降級(jí)為非線性的字典存儲(chǔ)模式,這樣雖然降低了查找速度,但是卻提升了修改對(duì)象的屬性的速度。

堆空間和??臻g

??臻g

  •  現(xiàn)代語言都是基于函數(shù)的,每個(gè)函數(shù)在執(zhí)行過程中,都有自己的生命周期和作用域,當(dāng)函數(shù)執(zhí)行結(jié)束時(shí),其作用域也會(huì)被銷毀,因此,我們會(huì)使用棧這種數(shù)據(jù)結(jié)構(gòu)來管理函數(shù)的調(diào)用過程,我們也把管理函數(shù)調(diào)用過程的棧結(jié)構(gòu)稱之為調(diào)用棧。
  •  棧空間主要是用來管理 JavaScript 函數(shù)調(diào)用的,棧是內(nèi)存中連續(xù)的一塊空間,同時(shí)棧結(jié)構(gòu)是“先進(jìn)后出”的策略。在函數(shù)調(diào)用過程中,涉及到上下文相關(guān)的內(nèi)容都會(huì)存放在棧上,比如原生類型、引用到的對(duì)象的地址、函數(shù)的執(zhí)行狀態(tài)、this 值等都會(huì)存在在棧上。當(dāng)一個(gè)函數(shù)執(zhí)行結(jié)束,那么該函數(shù)的執(zhí)行上下文便會(huì)被銷毀掉。
  •  ??臻g的最大的特點(diǎn)是空間連續(xù),所以在棧中每個(gè)元素的地址都是固定的,因此??臻g的查找效率非常高,但是通常在內(nèi)存中,很難分配到一塊很大的連續(xù)空間,因此,V8 對(duì)??臻g的大小做了限制,如果函數(shù)調(diào)用層過深,那么 V8 就有可能拋出棧溢出的錯(cuò)誤。
  •  棧的優(yōu)勢(shì)和缺點(diǎn):
    • 棧的結(jié)構(gòu)非常適合函數(shù)調(diào)用過程。
    • 在棧上分配資源和銷毀資源的速度非???,這主要?dú)w結(jié)于??臻g是連續(xù)的,分配空間和銷毀空間只需要移動(dòng)下指針就可以了。
    • 雖然操作速度非常快,但是棧也是有缺點(diǎn)的,其中最大的缺點(diǎn)也是它的優(yōu)點(diǎn)所造成的,那就是棧是連續(xù)的,所以要想在內(nèi)存中分配一塊連續(xù)的大空間是非常難的,因此??臻g是有限的。     
 
 
 
 
  1. // 棧溢出  
  2.       function factorial(n) {  
  3.         if (n === 1) {  
  4.           return 1;  
  5.         }  
  6.         return n * factorial(n - 1);  
  7.       }  
  8.       console.log(factorial(50000)); 

堆空間

  •  堆空間是一種樹形的存儲(chǔ)結(jié)構(gòu),用來存儲(chǔ)對(duì)象類型的離散的數(shù)據(jù),JavaScript 中除了原生類型的數(shù)據(jù),其他的都是對(duì)象類型,諸如函數(shù)、數(shù)組,在瀏覽器中還有 window 對(duì)象、document 對(duì)象等,這些都是存在堆空間的。
  •  宿主在啟動(dòng) V8 的過程中,會(huì)同時(shí)創(chuàng)建堆空間和??臻g,再繼續(xù)往下執(zhí)行,產(chǎn)生的新數(shù)據(jù)都會(huì)存放在這兩個(gè)空間中。

繼承

繼承就是一個(gè)對(duì)象可以訪問另外一個(gè)對(duì)象中的屬性和方法,在 JavaScript 中,我們通過原型和原型鏈的方式來實(shí)現(xiàn)了繼承特性。

JavaScript 的每個(gè)對(duì)象都包含了一個(gè)隱藏屬性 __proto__ ,我們就把該隱藏屬性 __proto__ 稱之為該對(duì)象的原型 (prototype),__proto__ 指向了內(nèi)存中的另外一個(gè)對(duì)象,我們就把 __proto__ 指向的對(duì)象稱為該對(duì)象的原型對(duì)象,那么該對(duì)象就可以直接訪問其原型對(duì)象的方法或者屬性。??

JavaScript 中的繼承非常簡(jiǎn)潔,就是每個(gè)對(duì)象都有一個(gè)原型屬性,該屬性指向了原型對(duì)象,查找屬性的時(shí)候,JavaScript 虛擬機(jī)會(huì)沿著原型一層一層向上查找,直至找到正確的屬性。

隱藏屬性__proto__

 
 
 
 
  1. var animal = {  
  2.   type: 'Default',  
  3.   color: 'Default',  
  4.   getInfo: function () {  
  5.     return `Type is: ${this.type},color is ${this.color}.`;  
  6.   },  
  7. };  
  8. var dog = {  
  9.   type: 'Dog',  
  10.   color: 'Black',  
  11. }; 

利用__proto__實(shí)現(xiàn)繼承:

 
 
 
 
  1. dog.__proto__ = animal;  
  2. dog.getInfo(); 

通常隱藏屬性是不能使用 JavaScript 來直接與之交互的。雖然現(xiàn)代瀏覽器都開了一個(gè)口子,讓 JavaScript 可以訪問隱藏屬性 __proto__,但是在實(shí)際項(xiàng)目中,我們不應(yīng)該直接通過 __proto__ 來訪問或者修改該屬性,其主要原因有兩個(gè):

  •  首先,這是隱藏屬性,并不是標(biāo)準(zhǔn)定義的;
  •  其次,使用該屬性會(huì)造成嚴(yán)重的性能問題。因?yàn)?JavaScript 通過隱藏類優(yōu)化了很多原有的對(duì)象結(jié)構(gòu),所以通過直接修改__proto__會(huì)直接破壞現(xiàn)有已經(jīng)優(yōu)化的結(jié)構(gòu),觸發(fā) V8 重構(gòu)該對(duì)象的隱藏類!

構(gòu)造函數(shù)是怎么創(chuàng)建對(duì)象的??

在 JavaScript 中,使用 new 加上構(gòu)造函數(shù)的這種組合來創(chuàng)建對(duì)象和實(shí)現(xiàn)對(duì)象的繼承。不過使用這種方式隱含的語義過于隱晦。其實(shí)是 JavaScript 為了吸引 Java 程序員、在語法層面去蹭 Java 熱點(diǎn),所以就被硬生生地強(qiáng)制加入了非常不協(xié)調(diào)的關(guān)鍵字 new。

 
 
 
 
  1. function DogFactory(type, color) {  
  2.   this.type = type;  
  3.   this.color = color;  
  4. }  
  5. var dog = new DogFactory('Dog', 'Black'); 

其實(shí)當(dāng) V8 執(zhí)行上面這段代碼時(shí),V8 在背后悄悄地做了以下幾件事情:

 
 
 
 
  1. var dog = {};  
  2. dog.__proto__ = DogFactory.prototype;  
  3. DogFactory.call(dog, 'Dog', 'Black'); 

機(jī)器碼、字節(jié)碼

V8 為什么要引入字節(jié)碼

  •  早期的 V8 為了提升代碼的執(zhí)行速度,直接將 JavaScript 源代碼編譯成了沒有優(yōu)化的二進(jìn)制機(jī)器代碼,如果某一段二進(jìn)制代碼執(zhí)行頻率過高,那么 V8 會(huì)將其標(biāo)記為熱點(diǎn)代碼,熱點(diǎn)代碼會(huì)被優(yōu)化編譯器優(yōu)化,優(yōu)化后的機(jī)器代碼執(zhí)行效率更高。
  •  隨著移動(dòng)設(shè)備的普及,V8 團(tuán)隊(duì)逐漸發(fā)現(xiàn)將 JavaScript 源碼直接編譯成二進(jìn)制代碼存在兩個(gè)致命的問題:
    • 時(shí)間問題:編譯時(shí)間過久,影響代碼啟動(dòng)速度;
    • 空間問題:緩存編譯后的二進(jìn)制代碼占用更多的內(nèi)存。
  •  這兩個(gè)問題無疑會(huì)阻礙 V8 在移動(dòng)設(shè)備上的普及,于是 V8 團(tuán)隊(duì)大規(guī)模重構(gòu)代碼,引入了中間的字節(jié)碼。字節(jié)碼的優(yōu)勢(shì)有如下三點(diǎn):
    •  解決啟動(dòng)問題:生成字節(jié)碼的時(shí)間很短;
    •  解決空間問題:字節(jié)碼雖然占用的空間比原始的 JavaScript 多,但是相較于機(jī)器代碼,字節(jié)碼還是小了太多,緩存字節(jié)碼會(huì)大大降低內(nèi)存的使用。
    •  代碼架構(gòu)清晰:采用字節(jié)碼,可以簡(jiǎn)化程序的復(fù)雜度,使得 V8 移植到不同的 CPU 架構(gòu)平臺(tái)更加容易。
  •  Bytecode 某種程度上就是匯編語言,只是它沒有對(duì)應(yīng)特定的 CPU,或者說它對(duì)應(yīng)的是虛擬的 CPU。這樣的話,生成 Bytecode 時(shí)簡(jiǎn)單很多,無需為不同的 CPU 生產(chǎn)不同的代碼。要知道,V8 支持 9 種不同的 CPU,引入一個(gè)中間層 Bytecode,可以簡(jiǎn)化 V8 的編譯流程,提高可擴(kuò)展性。
  •  如果我們?cè)诓煌布先ド?Bytecode,會(huì)發(fā)現(xiàn)生成代碼的指令是一樣的。

如何查看字節(jié)碼

 
 
 
 
  1. // test.js  
  2. function add(x, y) {  
  3.   var z = x + y;  
  4.   return z; 
  5.  }  
  6. console.log(add(1, 2)); 

運(yùn)行./d8 ./test.js --print-bytecode:

 
 
 
 
  1. [generated bytecode for function: add (0x01000824fe59 )]  
  2. Parameter count 3 #三個(gè)參數(shù),包括了顯式地傳入的 x 和 y,還有一個(gè)隱式地傳入的 this  
  3. Register count 1  
  4. Frame size 8  
  5.          0x10008250026 @    0 : 25 02             Ldar a1 #將a1寄存器中的值加載到累加器中,LoaD Accumulator from Register  
  6.          0x10008250028 @    2 : 34 03 00          Add a0, [0]  
  7.          0x1000825002b @    5 : 26 fb             Star r0 #Store Accumulator to Register,把累加器中的值保存到r0寄存器中  
  8.          0x1000825002d @    7 : aa                Return  #結(jié)束當(dāng)前函數(shù)的執(zhí)行,并將控制權(quán)傳回給調(diào)用方  
  9. Constant pool (size = 0)  
  10. Handler Table (size = 0)  
  11. Source Position Table (size = 0)  

常用字節(jié)碼指令:

  •  Ldar:表示將寄存器中的值加載到累加器中,你可以把它理解為 LoaD Accumulator from Register,就是把某個(gè)寄存器中的值,加載到累加器中。
  •  Star:表示 Store Accumulator Register, 你可以把它理解為 Store Accumulator to Register,就是把累加器中的值保存到某個(gè)寄存器中
  •  Add:Add a0, [0]是從 a0 寄存器加載值并將其與累加器中的值相加,然后將結(jié)果再次放入累加器。

          add a0 后面的[0]稱之為 feedback vector slot,又叫反饋向量槽,它是一個(gè)數(shù)組,解釋器將解釋執(zhí)行過程中的一些數(shù)據(jù)類型的分析信息都保存在這個(gè)反饋向量槽中了,目的是為了給 TurboFan 優(yōu)化編譯器提供優(yōu)化信息,很多字節(jié)碼都會(huì)為反饋向量槽提供運(yùn)行時(shí)信息。

  •  LdaSmi:將小整數(shù)(Smi)加載到累加器寄存器中
  •  Return:結(jié)束當(dāng)前函數(shù)的執(zhí)行,并將控制權(quán)傳回給調(diào)用方。返回的值是累加器中的值。

隱藏類和內(nèi)聯(lián)緩存

JavaScript 是一門動(dòng)態(tài)語言,其執(zhí)行效率要低于靜態(tài)語言,V8 為了提升 JavaScript 的執(zhí)行速度,借鑒了很多靜態(tài)語言的特性,比如實(shí)現(xiàn)了 JIT 機(jī)制,為了提升對(duì)象的屬性訪問速度而引入了隱藏類,為了加速運(yùn)算而引入了內(nèi)聯(lián)緩存。

為什么靜態(tài)語言的效率更高???

靜態(tài)語言中,如 C++ 在聲明一個(gè)對(duì)象之前需要定義該對(duì)象的結(jié)構(gòu),代碼在執(zhí)行之前需要先被編譯,編譯的時(shí)候,每個(gè)對(duì)象的形狀都是固定的,也就是說,在代碼的執(zhí)行過程中是無法被改變的。可以直接通過偏移量查詢來查詢對(duì)象的屬性值,這也就是靜態(tài)語言的執(zhí)行效率高的一個(gè)原因。??

JavaScript 在運(yùn)行時(shí),對(duì)象的屬性是可以被修改的,所以當(dāng) V8 使用了一個(gè)對(duì)象時(shí),比如使用了 obj.x 的時(shí)候,它并不知道該對(duì)象中是否有 x,也不知道 x 相對(duì)于對(duì)象的偏移量是多少,也就是說 V8 并不知道該對(duì)象的具體的形狀。那么,當(dāng)在 JavaScript 中要查詢對(duì)象 obj 中的 x 屬性時(shí),V8 會(huì)按照具體的規(guī)則一步一步來查詢,這個(gè)過程非常的慢且耗時(shí)。

將靜態(tài)的特性引入到 V8

  •  V8 采用的一個(gè)思路就是將 JavaScript 中的對(duì)象靜態(tài)化,也就是 V8 在運(yùn)行 JavaScript 的過程中,會(huì)假設(shè) JavaScript 中的對(duì)象是靜態(tài)的。
  •  具體地講,V8 對(duì)每個(gè)對(duì)象做如下兩點(diǎn)假設(shè):
    •  對(duì)象創(chuàng)建好了之后就不會(huì)添加新的屬性;
    •   對(duì)象創(chuàng)建好了之后也不會(huì)刪除屬性。
  •  符合這兩個(gè)假設(shè)之后,V8 就可以對(duì) JavaScript 中的對(duì)象做深度優(yōu)化了。V8 會(huì)為每個(gè)對(duì)象創(chuàng)建一個(gè)隱藏類,對(duì)象的隱藏類中記錄了該對(duì)象一些基礎(chǔ)的布局信息,包括以下兩點(diǎn):
    •  對(duì)象中所包含的所有的屬性;
    •  每個(gè)屬性相對(duì)于對(duì)象的偏移量。
  •  有了隱藏類之后,那么當(dāng) V8 訪問某個(gè)對(duì)象中的某個(gè)屬性時(shí),就會(huì)先去隱藏類中查找該屬性相對(duì)于它的對(duì)象的偏移量,有了偏移量和屬性類型,V8 就可以直接去內(nèi)存中取出對(duì)應(yīng)的屬性值,而不需要經(jīng)歷一系列的查找過程,那么這就大大提升了 V8 查找對(duì)象的效率。
  •  在 V8 中,把隱藏類又稱為 map,每個(gè)對(duì)象都有一個(gè) map 屬性,其值指向內(nèi)存中的隱藏類;
  •  map 描述了對(duì)象的內(nèi)存布局,比如對(duì)象都包括了哪些屬性,這些數(shù)據(jù)對(duì)應(yīng)于對(duì)象的偏移量是多少。

通過 d8 查看隱藏類

 
 
 
 
  1. // test.js  
  2. let point1 = { x: 100, y: 200 };  
  3. let point2 = { x: 200, y: 300 };  
  4. let point3 = { x: 100 };  
  5. %DebugPrint(point1);  
  6. %DebugPrint(point2);  
  7. %DebugPrint(point3);  
 
 
 
 
  1. ./d8 --allow-natives-syntax ./test.js  
 
 
 
 
  1. # ===============  
  2. DebugPrint: 0x1ea3080c5bc5: [JS_OBJECT_TYPE]  
  3. # V8 為 point1 對(duì)象創(chuàng)建的隱藏類  
  4.  - map: 0x1ea308284ce9  [FastProperties]  
  5.  - prototype: 0x1ea308241395   
  6.  - elements: 0x1ea3080406e9  [HOLEY_ELEMENTS]  
  7.  - properties: 0x1ea3080406e9  {  
  8.     #x: 100 (const data field 0)  
  9.     #y: 200 (const data field 1)  
  10.  }  
  11. 0x1ea308284ce9: [Map]  
  12.  - type: JS_OBJECT_TYPE  
  13.  - instance size: 20  
  14.  - inobject properties: 2  
  15.  - elements kind: HOLEY_ELEMENTS  
  16.  - unused property fields: 0  
  17.  - enum length: invalid  
  18.  - stable_map  
  19.  - back pointer: 0x1ea308284cc1   
  20.  - prototype_validity cell: 0x1ea3081c0451   
  21.  - instance descriptors (own) #2: 0x1ea3080c5bf5   
  22.  - prototype: 0x1ea308241395   
  23.  - constructor: 0x1ea3082413b1   
  24.  - dependent code: 0x1ea3080401ed   
  25.  - construction counter: 0  
  26. # ===============  
  27. DebugPrint: 0x1ea3080c5c1d: [JS_OBJECT_TYPE]  
  28. # V8 為 point2 對(duì)象創(chuàng)建的隱藏類  
  29.  - map: 0x1ea308284ce9  [FastProperties]  
  30.  - prototype: 0x1ea308241395   
  31.  - elements: 0x1ea3080406e9  [HOLEY_ELEMENTS]  
  32.  - properties: 0x1ea3080406e9  {  
  33.     #x: 200 (const data field 0)  
  34.     #y: 300 (const data field 1)  
  35.  }  
  36. 0x1ea308284ce9: [Map]  
  37.  - type: JS_OBJECT_TYPE  
  38.  - instance size: 20  
  39.  - inobject properties: 2  
  40.  - elements kind: HOLEY_ELEMENTS  
  41.  - unused property fields: 0  
  42.  - enum length: invalid  
  43.  - stable_map  
  44.  - back pointer: 0x1ea308284cc1   
  45.  - prototype_validity cell: 0x1ea3081c0451   
  46.  - instance descriptors (own) #2: 0x1ea3080c5bf5   
  47.  - prototype: 0x1ea308241395   
  48.  - constructor: 0x1ea3082413b1   
  49.  - dependent code: 0x1ea3080401ed   
  50.  - construction counter: 0  
  51. # ===============  
  52. DebugPrint: 0x1ea3080c5c31: [JS_OBJECT_TYPE]  
  53. # V8 為 point3 對(duì)象創(chuàng)建的隱藏類  
  54.  - map: 0x1ea308284d39  [FastProperties]  
  55.  - prototype: 0x1ea308241395   
  56.  - elements: 0x1ea3080406e9  [HOLEY_ELEMENTS]  
  57.  - properties: 0x1ea3080406e9  {  
  58.     #x: 100 (const data field 0)  
  59.  }  
  60. 0x1ea308284d39: [Map]  
  61.  - type: JS_OBJECT_TYPE  
  62.  - instance size: 16  
  63.  - inobject properties: 1  
  64.  - elements kind: HOLEY_ELEMENTS  
  65.  - unused property fields: 0  
  66.  - enum length: invalid  
  67.  - stable_map  
  68.  - back pointer: 0x1ea308284d11   
  69.  - prototype_validity cell: 0x1ea3081c0451   
  70.  - instance descriptors (own) #1: 0x1ea3080c5c41   
  71.  - prototype: 0x1ea308241395   
  72.  - constructor: 0x1ea3082413b1   
  73.  - dependent code: 0x1ea3080401ed   
  74.  - construction counter: 0 

多個(gè)對(duì)象共用一個(gè)隱藏類

  •  在 V8 中,每個(gè)對(duì)象都有一個(gè) map 屬性,該屬性值指向該對(duì)象的隱藏類。不過如果兩個(gè)對(duì)象的形狀是相同的,V8 就會(huì)為其復(fù)用同一個(gè)隱藏類,這樣有兩個(gè)好處:
    •  減少隱藏類的創(chuàng)建次數(shù),也間接加速了代碼的執(zhí)行速度;
    •  減少了隱藏類的存儲(chǔ)空間。
  •  那么,什么情況下兩個(gè)對(duì)象的形狀是相同的,要滿足以下兩點(diǎn):
    •  相同的屬性名稱;
    •  相等的屬性個(gè)數(shù)。

重新構(gòu)建隱藏類

  •  給一個(gè)對(duì)象添加新的屬性,刪除新的屬性,或者改變某個(gè)屬性的數(shù)據(jù)類型都會(huì)改變這個(gè)對(duì)象的形狀,那么勢(shì)必也就會(huì)觸發(fā) V8 為改變形狀后的對(duì)象重建新的隱藏類。 
 
 
 
 
  1. // test.js  
  2. let point = {};  
  3. %DebugPrint(point);  
  4. point.x = 100;  
  5. %DebugPrint(point);  
  6. point.y = 200;  
  7. %DebugPrint(point);  
 
 
 
 
  1. # ./d8 --allow-natives-syntax ./test.js  
  2. DebugPrint: 0x32c7080c5b2d: [JS_OBJECT_TYPE]  
  3.  - map: 0x32c7082802d9  [FastProperties]  
  4.  ...  
  5. DebugPrint: 0x32c7080c5b2d: [JS_OBJECT_TYPE]  
  6.  - map: 0x32c708284cc1  [FastProperties]  
  7.  ...  
  8. DebugPrint: 0x32c7080c5b2d: [JS_OBJECT_TYPE]  
  9.  - map: 0x32c708284ce9  [FastProperties]  
    新聞名稱:瀏覽器是如何工作的:Chrome V8讓你更懂JavaScript
    鏈接分享:http://uogjgqi.cn/article/dpsipdg.html
掃二維碼與項(xiàng)目經(jīng)理溝通

我們?cè)谖⑿派?4小時(shí)期待你的聲音

解答本文疑問/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流