掃二維碼與項(xiàng)目經(jīng)理溝通
我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流
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)存以及垃圾回收。
- 1000100111011000 #機(jī)器指令
- mov ax,bx #匯編指令
資料拓展: 匯編語言入門教程【阮一峰】 | 理解 V8 的字節(jié)碼「譯」
https://zhuanlan.zhihu.com/p/28590489
熱門 JavaScript 引擎
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
https://storage.googleapis.com/chromium-v8/official/canary/v8-mac64-dbg-8.4.109.zip
https://storage.googleapis.com/chromium-v8/official/canary/v8-linux32-dbg-8.4.109.zip
https://storage.googleapis.com/chromium-v8/official/canary/v8-linux64-dbg-8.4.109.zip
https://storage.googleapis.com/chromium-v8/official/canary/v8-win32-dbg-8.4.109.zip
https://storage.googleapis.com/chromium-v8/official/canary/v8-win64-dbg-8.4.109.zip
- // 解壓文件,點(diǎn)擊d8打開(mac安全策略限制的話,按住control,再點(diǎn)擊,彈出菜單中選擇打開)
- V8 version 8.4.109
- d8> 1 + 2
- 3
- d8> 2 + '4'
- "24"
- d8> console.log(23)
- 23
- undefined
- d8> var a = 1
- undefined
- d8> a + 2
- 3
- d8> this
- [object global]
- d8>
本文后續(xù)用于 demo 演示時(shí)的文件目錄結(jié)構(gòu):
- V8:
- # d8可執(zhí)行文件
- d8
- icudtl.dat
- libc++.dylib
- libchrome_zlib.dylib
- libicui18n.dylib
- libicuuc.dylib
- libv8.dylib
- libv8_debug_helper.dylib
- libv8_for_testing.dylib
- libv8_libbase.dylib
- libv8_libplatform.dylib
- obj
- snapshot_blob.bin
- v8_build_config.json
- # 新建的js示例文件
- test.js
- # 如果已有HomeBrew,忽略第一條命令
- ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
- brew install v8
都有哪些 d8 命令可供使用?
- # 如果不想使用./d8這種方式進(jìn)行調(diào)試,可將d8加入環(huán)境變量,之后就可以直接`d8 --help`了
- ./d8 --help
- # 如果是 Windows 系統(tǒng),可能缺少 grep 程序,請(qǐng)自行下載安裝并添加環(huán)境變量
- ./d8 --help |grep print
使用 d8 進(jìn)行調(diào)試
- // test.js
- function sum(a) {
- var b = 6;
- return a + 6;
- }
- console.log(sum(3));
- # d8 后面跟上文件名和要執(zhí)行的命令,如執(zhí)行下面這行命令,就會(huì)打印出 test.js 文件所生成的字節(jié)碼。
- ./d8 ./test.js --print-bytecode
- # 執(zhí)行以下命令,輸出9
- ./d8 ./test.js
內(nèi)部方法
你還可以使用 V8 所提供的一些內(nèi)部方法,只需要在啟動(dòng) V8 時(shí)傳入 --allow-natives-syntax 命令,你就可以在 test.js 中使用諸如HasFastProperties(檢查一個(gè)對(duì)象是否擁有快屬性)的內(nèi)部方法(索引屬性、常規(guī)屬性、快屬性等下文會(huì)介紹)。
- function Foo(property_num, element_num) {
- //添加可索引屬性
- for (let i = 0; i < element_num; i++) {
- this[i] = `element${i}`;
- }
- //添加常規(guī)屬性
- for (let i = 0; i < property_num; i++) {
- let ppt = `property${i}`;
- this[ppt] = ppt;
- }
- }
- var bar = new Foo(10, 10);
- // 檢查一個(gè)對(duì)象是否擁有快屬性
- console.log(%HasFastProperties(bar));
- delete bar.property2;
- console.log(%HasFastProperties(bar));
- ./d8 --allow-natives-syntax ./test.js
- # 依次打?。簍rue false
心似雙絲網(wǎng),中有千千結(jié)
V8 引擎的內(nèi)部結(jié)構(gòu)
V8 是一個(gè)非常復(fù)雜的項(xiàng)目,有超過 100 萬行 C++代碼。它由許多子模塊構(gòu)成,其中這 4 個(gè)模塊是最重要的:
通常有兩種類型的解釋器,基于棧 (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):
其中,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í)際上是匯編代碼)。
圖片中的紅色虛線是逆向的,也就是說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。
- function add(x, y) {
- return x + y;
- }
- add(3, 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 執(zhí)行一段 JavaScript 代碼所經(jīng)歷的主要流程包括:
一等公民與閉包
一等公民的定義
動(dòng)態(tài)作用域與靜態(tài)作用域
閉包的三個(gè)基礎(chǔ)特性
- // 閉包(靜態(tài)作用域,一等公民,調(diào)用棧的矛盾體)
- function foo() {
- var d = 20;
- return function inner(a, b) {
- const c = a + b + d;
- return c;
- };
- }
- const f = foo();
關(guān)于閉包,可參考我以前的一篇文章,在此不再贅述,在此主要談下閉包給 Chrome V8 帶來的問題及其解決策略。
惰性解析??
所謂惰性解析是指解析器在解析的過程中,如果遇到函數(shù)聲明,那么會(huì)跳過函數(shù)內(nèi)部的代碼,并不會(huì)為其生成 AST 和字節(jié)碼,而僅僅生成頂層代碼的 AST 和字節(jié)碼。
預(yù)解析器
V8 引入預(yù)解析器,比如當(dāng)解析頂層代碼的時(shí)候,遇到了一個(gè)函數(shù),那么預(yù)解析器并不會(huì)直接跳過該函數(shù),而是對(duì)該函數(shù)做一次快速的預(yù)解析。
V8 內(nèi)部是如何存儲(chǔ)對(duì)象的:快屬性和慢屬性
下面的代碼會(huì)輸出什么:
- // test.js
- function Foo() {
- this[200] = 'test-200';
- this[1] = 'test-1';
- this[100] = 'test-100';
- this['B'] = 'bar-B';
- this[50] = 'test-50';
- this[9] = 'test-9';
- this[8] = 'test-8';
- this[3] = 'test-3';
- this[5] = 'test-5';
- this['D'] = 'bar-D';
- this['C'] = 'bar-C';
- }
- var bar = new Foo();
- for (key in bar) {
- console.log(`index:${key} value:${bar[key]}`);
- }
- //輸出:
- // index:1 value:test-1
- // index:3 value:test-3
- // index:5 value:test-5
- // index:8 value:test-8
- // index:9 value:test-9
- // index:50 value:test-50
- // index:100 value:test-100
- // index:200 value:test-200
- // index:B value:bar-B
- // index:D value:bar-D
- // 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è)。
- function Foo(property_num, element_num) {
- //添加可索引屬性
- for (let i = 0; i < element_num; i++) {
- this[i] = `element${i}`;
- }
- //添加常規(guī)屬性
- for (let i = 0; i < property_num; i++) {
- let ppt = `property${i}`;
- this[ppt] = ppt;
- }
- }
- 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
- // 棧溢出
- function factorial(n) {
- if (n === 1) {
- return 1;
- }
- return n * factorial(n - 1);
- }
- console.log(factorial(50000));
堆空間
繼承
繼承就是一個(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__
- var animal = {
- type: 'Default',
- color: 'Default',
- getInfo: function () {
- return `Type is: ${this.type},color is ${this.color}.`;
- },
- };
- var dog = {
- type: 'Dog',
- color: 'Black',
- };
利用__proto__實(shí)現(xiàn)繼承:
- dog.__proto__ = animal;
- dog.getInfo();
通常隱藏屬性是不能使用 JavaScript 來直接與之交互的。雖然現(xiàn)代瀏覽器都開了一個(gè)口子,讓 JavaScript 可以訪問隱藏屬性 __proto__,但是在實(shí)際項(xiàng)目中,我們不應(yīng)該直接通過 __proto__ 來訪問或者修改該屬性,其主要原因有兩個(gè):
構(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。
- function DogFactory(type, color) {
- this.type = type;
- this.color = color;
- }
- var dog = new DogFactory('Dog', 'Black');
其實(shí)當(dāng) V8 執(zhí)行上面這段代碼時(shí),V8 在背后悄悄地做了以下幾件事情:
- var dog = {};
- dog.__proto__ = DogFactory.prototype;
- DogFactory.call(dog, 'Dog', 'Black');
機(jī)器碼、字節(jié)碼
V8 為什么要引入字節(jié)碼
如何查看字節(jié)碼
- // test.js
- function add(x, y) {
- var z = x + y;
- return z;
- }
- console.log(add(1, 2));
運(yùn)行./d8 ./test.js --print-bytecode:
- [generated bytecode for function: add (0x01000824fe59
)] - Parameter count 3 #三個(gè)參數(shù),包括了顯式地傳入的 x 和 y,還有一個(gè)隱式地傳入的 this
- Register count 1
- Frame size 8
- 0x10008250026 @ 0 : 25 02 Ldar a1 #將a1寄存器中的值加載到累加器中,LoaD Accumulator from Register
- 0x10008250028 @ 2 : 34 03 00 Add a0, [0]
- 0x1000825002b @ 5 : 26 fb Star r0 #Store Accumulator to Register,把累加器中的值保存到r0寄存器中
- 0x1000825002d @ 7 : aa Return #結(jié)束當(dāng)前函數(shù)的執(zhí)行,并將控制權(quán)傳回給調(diào)用方
- Constant pool (size = 0)
- Handler Table (size = 0)
- Source Position Table (size = 0)
- 3
常用字節(jié)碼指令:
add a0 后面的[0]稱之為 feedback vector slot,又叫反饋向量槽,它是一個(gè)數(shù)組,解釋器將解釋執(zhí)行過程中的一些數(shù)據(jù)類型的分析信息都保存在這個(gè)反饋向量槽中了,目的是為了給 TurboFan 優(yōu)化編譯器提供優(yōu)化信息,很多字節(jié)碼都會(huì)為反饋向量槽提供運(yùn)行時(shí)信息。
隱藏類和內(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
通過 d8 查看隱藏類
- // test.js
- let point1 = { x: 100, y: 200 };
- let point2 = { x: 200, y: 300 };
- let point3 = { x: 100 };
- %DebugPrint(point1);
- %DebugPrint(point2);
- %DebugPrint(point3);
- ./d8 --allow-natives-syntax ./test.js
- # ===============
- DebugPrint: 0x1ea3080c5bc5: [JS_OBJECT_TYPE]
- # V8 為 point1 對(duì)象創(chuàng)建的隱藏類
- - map: 0x1ea308284ce9
- - prototype: 0x1ea308241395
- - elements: 0x1ea3080406e9
[HOLEY_ELEMENTS] - - properties: 0x1ea3080406e9
{ - #x: 100 (const data field 0)
- #y: 200 (const data field 1)
- }
- 0x1ea308284ce9: [Map]
- - type: JS_OBJECT_TYPE
- - instance size: 20
- - inobject properties: 2
- - elements kind: HOLEY_ELEMENTS
- - unused property fields: 0
- - enum length: invalid
- - stable_map
- - back pointer: 0x1ea308284cc1
- - prototype_validity cell: 0x1ea3081c0451
| - - instance descriptors (own) #2: 0x1ea3080c5bf5
- - prototype: 0x1ea308241395
- - constructor: 0x1ea3082413b1
- - dependent code: 0x1ea3080401ed
- - construction counter: 0
- # ===============
- DebugPrint: 0x1ea3080c5c1d: [JS_OBJECT_TYPE]
- # V8 為 point2 對(duì)象創(chuàng)建的隱藏類
- - map: 0x1ea308284ce9
- - prototype: 0x1ea308241395
- - elements: 0x1ea3080406e9
[HOLEY_ELEMENTS] - - properties: 0x1ea3080406e9
{ - #x: 200 (const data field 0)
- #y: 300 (const data field 1)
- }
- 0x1ea308284ce9: [Map]
- - type: JS_OBJECT_TYPE
- - instance size: 20
- - inobject properties: 2
- - elements kind: HOLEY_ELEMENTS
- - unused property fields: 0
- - enum length: invalid
- - stable_map
- - back pointer: 0x1ea308284cc1
- - prototype_validity cell: 0x1ea3081c0451
| - - instance descriptors (own) #2: 0x1ea3080c5bf5
- - prototype: 0x1ea308241395
- - constructor: 0x1ea3082413b1
- - dependent code: 0x1ea3080401ed
- - construction counter: 0
- # ===============
- DebugPrint: 0x1ea3080c5c31: [JS_OBJECT_TYPE]
- # V8 為 point3 對(duì)象創(chuàng)建的隱藏類
- - map: 0x1ea308284d39
- - prototype: 0x1ea308241395
- - elements: 0x1ea3080406e9
[HOLEY_ELEMENTS] - - properties: 0x1ea3080406e9
{ - #x: 100 (const data field 0)
- }
- 0x1ea308284d39: [Map]
- - type: JS_OBJECT_TYPE
- - instance size: 16
- - inobject properties: 1
- - elements kind: HOLEY_ELEMENTS
- - unused property fields: 0
- - enum length: invalid
- - stable_map
- - back pointer: 0x1ea308284d11
- - prototype_validity cell: 0x1ea3081c0451
| - - instance descriptors (own) #1: 0x1ea3080c5c41
- - prototype: 0x1ea308241395
- - constructor: 0x1ea3082413b1
- - dependent code: 0x1ea3080401ed
- - construction counter: 0
多個(gè)對(duì)象共用一個(gè)隱藏類
重新構(gòu)建隱藏類
- // test.js
- let point = {};
- %DebugPrint(point);
- point.x = 100;
- %DebugPrint(point);
- point.y = 200;
- %DebugPrint(point);
- # ./d8 --allow-natives-syntax ./test.js
- DebugPrint: 0x32c7080c5b2d: [JS_OBJECT_TYPE]
- - map: 0x32c7082802d9
- ...
- DebugPrint: 0x32c7080c5b2d: [JS_OBJECT_TYPE]
- - map: 0x32c708284cc1
- ...
- DebugPrint: 0x32c7080c5b2d: [JS_OBJECT_TYPE]
- - map: 0x32c708284ce9

我們?cè)谖⑿派?4小時(shí)期待你的聲音
解答本文疑問/技術(shù)咨詢/運(yùn)營(yíng)咨詢/技術(shù)建議/互聯(lián)網(wǎng)交流