V8 Object 內存結構與屬性訪問詳解
V8 Object 內存結構與屬性訪問詳解 從屬于筆者的 Web 前端入門與工程實踐 ,推薦閱讀 2016-我的前端之路:工具化與工程化 。更多關于 JavaScript 引擎文章參考 這里 。
V8 Object 內存結構與屬性訪問
上世紀九十年代,隨著網景瀏覽器的發行,JavaScript 首次進入人們的視線。之后隨著 AJAX 的大規模應用與富客戶端、單頁應用時代的到來,JavaScript 在 Web 開發中占據了越來越重要的地位。在早期的 JavaScript 引擎中,性能越發成為了開發網頁應用的瓶頸。而 V8 引擎設計的目標就是為了保證大型 JavaScript 應用的執行效率,在 很多測試 中可以明顯發現 V8 的性能優于 JScript (Internet Explorer), SpiderMonkey (Firefox), 以及 JavaScriptCore(Safari). 根據 V8 的官方文檔介紹,其主要是從屬性訪問、動態機器碼生成以及高效的垃圾回收這三個方面著手性能優化。Obejct 當屬 JavaScript 最重要的數據類型之一,本文我們對其內部結構進行詳細闡述。其繼承關系圖如下所示:
在 V8 中新分配的 JavaScript 對象結構如下所示:
[ class / map ] -> ... ; 指向內部類
[ properties ] -> [empty array]
[ elements ] -> [empty array] ; 數值類型名稱的屬性
[ reserved #1 ] -\
[ reserved #2 ] |
[ reserved #3 ] }- in object properties,即預分配的內存空間
............... |
[ reserved #N ] -/
在創建新的對象時,V8 會創建某個預分配的內存區域來存放所謂的 in-object 屬性,預分配區域的大小由構造函數中的參數數目決定(this.field = expr)。當你打算向對象中添加某個新屬性時,V8 首先會嘗試放入所謂的 in-order 槽位中,當 in-object 槽位過載之后,V8 會嘗試將新的屬性添加到 out-of-object 屬性列表。而屬性名與屬性下標的映射關系即存放在所謂隱藏類中,譬如{ a: 1, b: 2, c: 3, d: 4}對象的存儲方式可能如下:
[ class ] -> [a: in obj #1, b: in obj #2, c: out obj #1, d: out obj #2]
[ properties ] -> [ 3 ][ 4 ] ; this is linear array
[ elements ]
[ 1 ]
[ 2 ]
隨著屬性數目的增加,V8 會轉回到傳統的字典模式/哈希表模式:
[ class ] -> [ OBJECT IS IN DICTIONARY MODE ]
[ properties ] -> [a: 1, b: 2, c: 3, d: 4, e: 5] ; this is classical hash table
[ elements ]
Reference
Property Name:屬性名
作為動態語言,JavaScript 允許我們以非常靈活的方式來定義對象,譬如:
obj.prop
obj["prop"]
參照 JavaScript 定義規范中的描述,屬性名恒為字符串,即使你使用了某個非字符串的名字,也會隱式地轉化為字符串類型。譬如你創建的是個數組,以數值下標進行訪問,然而 V8 還是將其轉化為了字符串再進行索引,因此以下的方式就會獲得相同的效果:
obj[1]; //
obj["1"]; // names for the same property
obj[1.0]; //
var o = { toString: function () { return "-1.5"; } };
obj[-1.5]; // also equivalent
obj[o]; // since o is converted to string
而 JavaScript 中的 Array 只是包含了額外的length屬性的對象而已,length會返回當前最大下標加一的結果(此時字符串下標會被轉化為數值類型計算):
var a = new Array();
a[100] = "foo";
a.length; //101
a[undefined] = 'a';
a.length; //0
Function本質上也是對象,只不過length屬性會返回參數的長度而已:
> a = ()=>{}
[Function: a]
> a.length
0
> a = (b)=>{}
[Function: a]
> a.length
1
In-Object Properties & Fast Property Access:對象內屬性與訪問優化
作為動態類型語言,JavaScript 中的對象屬性可以在運行時動態地增刪,意味著整個對象的結構會頻繁地改變。大部分 JavaScript 引擎傾向于使用字典類型的數據結構來存放對象屬性( Object Properties),每次進行屬性訪問的時候引擎都需要在內層中先動態定位屬性對應的下標地址然后讀取值。這種方式實現上比較容易,但是會導致較差的性能表現。其他的類似于 Java 與 Smalltalk 這樣的靜態語言中,成員變量在編譯階段即確定了其在內存中的固定偏移地址,進行屬性訪問的時候只需要單指令從內存中加載即可。而 V8 則利用動態創建隱藏內部類的方式動態地將屬性的內存地址記錄在對象內,從而提升整體的屬性訪問速度。總結而言,每當為某個對象添加新的屬性時,V8 會自動修正其隱藏內部類。我們先通過某個 實驗 來感受下隱藏類的存在:
var PROPERTIES = 10000000;
var o = {};
var start = +new Date;
for (var i = 0; i < PROPERTIES; i++) {
o[i] = i;
}
console.log(+new Date - start);
function O(size) {
for (var i = 0; i < size; i++) {
this[i] = null;
}
}
var o = new O(PROPERTIES);
var start = +new Date;
for (var i = 0; i < PROPERTIES; i++) {
o[i] = i;
}
console.log(+new Date - start);
class OClass {
constructor(size){
for (var i = 0; i < size; i++) {
this[i] = null;
}
}
}
var o = new OClass(PROPERTIES);
var start = +new Date;
for (var i = 0; i < PROPERTIES; i++) {
o[i] = i;
}
console.log(+new Date - start);
該程序的執行結果如下:
// Babel 下結果
385
37
49
// Chrome 下結果
416
32
31
第一種實現中,每次為對象o設置新的屬性時,V8 都會創建新的隱藏內部類(內部稱為 Map)來存儲新的內存地址以優化屬性查找速度。而第二種實現時,我們在創建新的對象時即初始化了內部類,這樣在賦值屬性時 V8 以及能夠高性能地定位這些屬性。第三種實現則是用的 ES6 Class,在純正的 V8 下性能最好。接下來我們具體闡述下隱藏類的工作原理,假設我們定義了描述點的函數:
function Point(x, y) {
this.x = x;
this.y = y;
}
當我們執行new Point(x,y)語句時,V8 會創建某個新的Point對象。創建的過程中,V8 首先會創建某個所謂C0的隱藏內部類,因為尚未為對象添加任何屬性,此時隱藏類還是空的:
接下來調用首個賦值語句this.x = x;為當前Point對象創建了新的屬性x,此時 V8 會基于C0創建另一個隱藏類C1來替換C0,然后在C1中存放對象屬性x的內存位置信息:
這里從C0到C1的變化稱為轉換(Transitions),當我們為同一個類型的對象添加新的屬性時,并不是每次都會創建新的隱藏類,而是多個對象會共用某個符合轉換條件的隱藏類。接下來繼續執行this.y = y 這一條語句,會為Point對象創建新的屬性。此時 V8 會進行以下步驟:
-
基于C1創建另一個隱藏類C1,并且將關于屬性y的位置信息寫入到C2中。
-
更新C1為其添加轉換信息,即當為Point對象添加屬性 y 時,應該轉換到隱藏類 C2。
整個過程的偽代碼描述如下:
<Point object is allocated>
Class C0
"x": TRANSITION to C1 at offset 0
this.x = x;
Class C1
"x": FIELD at offset 0
"y": TRANSITION to C2 at offset 1
this.y = y;
Map C2
"x": FIELD at offset 0
"y": FIELD at offset 1
Reused Hidden Class:重復使用的隱藏類
我們在上文中提及,如果每次添加新的屬性時都創建新的隱藏類無疑是極大的性能浪費,實際上當我們再次創建新的Point對象時,V8 并不會創建新的隱藏類而是使用已有的,過程描述如下:
-
初始化新的Point對象,并將隱藏類指向C0。
-
添加x屬性時,遵循隱藏類的轉換原則指向到C1 , 并且根據C1指定的偏移地址寫入x。
-
添加y屬性時,遵循隱藏類的轉換原則指向到C2,并且根據C2指定的偏移地址寫入y。
另外我們在上文以鏈表的方式描述轉換,實際上真實場景中 V8 會以樹的結構來描述轉換及其之間的關系,這樣就能夠用于類似于下面的屬性一致而賦值順序顛倒的場景:
function Point(x, y, reverse) {
if (reverse) {
this.x = x;
this.y = y;
} else {
this.y = x;
this.x = y;
}
}
Methods & Prototypes:方法與原型
JavaScript 中并沒有類的概念(語法糖除外),因此對于方法的調用處理會難于 C++ 或者 Java。下面這個例子中,distance方法可以被看做Point的普通屬性之一,不過其并非原始類型的數據,而是指向了另一個函數:
function Point(x, y) {
this.x = x;
this.y = y;
this.distance = PointDistance;
}
function PointDistance(p) {
var dx = this.x - p.x;
var dy = this.y - p.y;
return Math.sqrt(dx*dx + dy*dy);
}
如果我們像上文介紹的普通的 in-object 域一樣來處理distance屬性,那么無疑會帶來較大的內存浪費,畢竟每個對象都要存放一段外部函數引用(Reference 的內存占用往往大于原始類型)。C++ 中則是以指向多個虛函數的虛函數表(V-Tables)解決這個問題。每個包含虛函數的類的實例都會指向這個虛函數表,當調用某個虛函數時,程序會自動從虛函數表中加載該函數的地址信息然后轉向到該地址調用。V8 中我們已經使用了隱藏類這一共享數據結構,因此可以很方便地改造下就可以。我們引入了所謂 Constant Functions 的概念,某個 Constant Function 即代表了對象中僅包含某個名字,而具體的屬性值存放在描述符本身的概念:
<Point object is allocated>
Class C0
"x": TRANSITION to C1 at offset 0
this.x = x;
Class C1
"x": FIELD at offset 0
"y": TRANSITION to C2 at offset 1
this.y = y;
Class C2
"x": FIELD at offset 0
"y": FIELD at offset 1
"distance": TRANSITION to C3 <PointDistance>
this.distance = PointDistance;
Class C3
"x": FIELD at offset 0
"y": FIELD at offset 1
"distance": CONSTANT_FUNCTION <PointDistance>
注意,在這里如果我們將PointDistance 重定義指向了其他函數,那么這個轉換也會自動失效,V8 會創建新的隱藏類。另一種解決這個問題的方法就是使用原型,每個構造函數都會有所謂的Prototype屬性,該屬性會自動成為對象的原型鏈上的一環,上面的例子可以改寫為以下方式:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.distance = function(p) {
var dx = this.x - p.x;
var dy = this.y - p.y;
return Math.sqrt(dx*dx + dy*dy);
}
...
var u = new Point(1, 2);
var v = new Point(3, 4);
var d = u.distance(v);
V8 同樣會把原型鏈上的方法在隱藏類中映射為 Constant Function 描述符,而調用原型方法往往會比調用自身方法慢一點,畢竟引擎不僅要去掃描自身的隱藏類,還要去掃描原型鏈上對象的隱藏類才能得知真正的函數調用地址。不過這個不會對于代碼的性能造成明顯的影響,因此寫代碼的時候也不必小心翼翼的避免這個。
Dictionary Mode
對于復雜屬性的對象,V8 會使用所謂的字典模式(Dictionary Mode)來存儲對象,也就是使用哈希表來存放鍵值信息,這種方式存儲開銷會小于上文提到的包含了隱藏類的方式,不過查詢速度會遠小于前者。初始狀態下,哈希表中的所有的鍵與值都被設置為了undefined,當插入新的數據時,計算得出的鍵名的哈希值的低位會被當做初始的存儲索引地址。如果此地址已經被占用了,V8 會嘗試向下一個地址進行插入,直到插入成功,偽代碼表述如下:
// 插入
insert(table, key, value):
table = ensureCapacity(table, length(table) + 1)
code = hash(key)
n = capacity(table)
index = code (mod n)
while getKey(table, index) is not undefined:
index += 1 (mod n)
set(table, index, key, value)
//查找
lookup(table, key):
code = hash(key)
n = capacity(table)
index = code (mod n)
k = getKey(table, index)
while k is not null or undefined
and k != key:
index += 1 (mod n)
k = getKey(table, index)
if k == key:
return getValue(table, index)
else:
return undefined
盡管計算鍵名哈希值與比較的速度會比較快,但是每次讀寫屬性的時候都進行這么多步驟無疑會大大拉低速度,因此 V8 盡可能地會避免使用這種存儲方式。
Fast Elements:數值下標的屬性
V8 中將屬性名為非負整數(0、1、2……)的屬性稱為Element,每個對象都有一個指向Element數組的指針,其存放和其他屬性是分開的。注意,隱藏類中并不包含 Element 的描述符,但可能包含其它有著不同 Element 類型的同一種隱藏類的轉換描述符。大多數情況下,對象都會有 Fast Element,也就是說這些 Element 以連續數組的形式存放。有三種不同的 Fast Element:
-
Fast small integers
-
Fast doubles
-
Fast values
根據標準,JavaScript 中的所有數字都理應以64位浮點數形式出現。因此 V8 盡可能以31位帶符號整數來表達數字(最低位總是0,這有助于垃圾回收器區分數字和指針)。因此含有Fast small integers類型的對象,其 Element 類型只會包含這樣的數字。如果需要存儲小數、大整數或其他特殊值,如-0,則需要將數組提升為 Fast doubles。于是這引入了潛在的昂貴的復制-轉換操作,但通常不會頻繁發生。Fast doubles 仍然是很快的,因為所有的數字都是無封箱存儲的。但如果我們要存儲的是其他類型,比如字符串或者對象,則必須將其提升為普通的 Fast Element 數組。
JavaScript 不提供任何確定存儲元素多少的辦法。你可能會說像這樣的辦法,new Array(100),但實際上這僅僅針對Array構造函數有用。如果你將值存在一個不存在的下標上,V8會重新開辟更大的內存,將原有元素復制到新內存。V8 可以處理帶空洞的數組,也就是只有某些下標是存有元素,而期間的下標都是空的。其內部會安插特殊的哨兵值,因此試圖訪問未賦值的下標,會得到undefined。當然,Fast Element 也有其限制。如果你在遠遠超過當前數組大小的下標賦值,V8 會將數組轉換為字典模式,將值以哈希表的形式存儲。這對于稀疏數組來說很有用,但性能上肯定打了折扣,無論是從轉換這一過程來說,還是從之后的訪問來說。如果你需要復制整個數組,不要逆向復制(索引從高到低),因為這幾乎必然觸發字典模式。
// 這會大大降低大數組的性能
function copy(a) {
var b = new Array();
for (var i = a.length - 1; i >= 0; i--)
b[i] = a[i];
return b;
}
由于普通的屬性和數字式屬性分開存放,即使數組退化為字典模式,也不會影響到其他屬性的訪問速度(反之亦然)。
Object 代碼聲明
// https://v8docs.nodesource.com/node-7.2/d4/da0/v8_8h_source.html#l02660
class V8_EXPORT Object : public Value {
public:
V8_DEPRECATE_SOON("Use maybe version",
bool Set(Local<Value> key, Local<Value> value));
V8_WARN_UNUSED_RESULT Maybe<bool> Set(Local<Context> context,
Local<Value> key, Local<Value> value);
V8_DEPRECATE_SOON("Use maybe version",
bool Set(uint32_t index, Local<Value> value));
V8_WARN_UNUSED_RESULT Maybe<bool> Set(Local<Context> context, uint32_t index,
Local<Value> value);
// Implements CreateDataProperty (ECMA-262, 7.3.4).
//
// Defines a configurable, writable, enumerable property with the given value
// on the object unless the property already exists and is not configurable
// or the object is not extensible.
//
// Returns true on success.
V8_WARN_UNUSED_RESULT Maybe<bool> CreateDataProperty(Local<Context> context,
Local<Name> key,
Local<Value> value);
V8_WARN_UNUSED_RESULT Maybe<bool> CreateDataProperty(Local<Context> context,
uint32_t index,
Local<Value> value);
// Implements DefineOwnProperty.
//
// In general, CreateDataProperty will be faster, however, does not allow
// for specifying attributes.
//
// Returns true on success.
V8_WARN_UNUSED_RESULT Maybe<bool> DefineOwnProperty(
Local<Context> context, Local<Name> key, Local<Value> value,
PropertyAttribute attributes = None);
// Sets an own property on this object bypassing interceptors and
// overriding accessors or read-only properties.
//
// Note that if the object has an interceptor the property will be set
// locally, but since the interceptor takes precedence the local property
// will only be returned if the interceptor doesn't return a value.
//
// Note also that this only works for named properties.
V8_DEPRECATED("Use CreateDataProperty / DefineOwnProperty",
bool ForceSet(Local<Value> key, Local<Value> value,
PropertyAttribute attribs = None));
V8_DEPRECATE_SOON("Use CreateDataProperty / DefineOwnProperty",
Maybe<bool> ForceSet(Local<Context> context,
Local<Value> key, Local<Value> value,
PropertyAttribute attribs = None));
V8_DEPRECATE_SOON("Use maybe version", Local<Value> Get(Local<Value> key));
V8_WARN_UNUSED_RESULT MaybeLocal<Value> Get(Local<Context> context,
Local<Value> key);
V8_DEPRECATE_SOON("Use maybe version", Local<Value> Get(uint32_t index));
V8_WARN_UNUSED_RESULT MaybeLocal<Value> Get(Local<Context> context,
uint32_t index);
V8_DEPRECATED("Use maybe version",
PropertyAttribute GetPropertyAttributes(Local<Value> key));
V8_WARN_UNUSED_RESULT Maybe<PropertyAttribute> GetPropertyAttributes(
Local<Context> context, Local<Value> key);
V8_DEPRECATED("Use maybe version",
Local<Value> GetOwnPropertyDescriptor(Local<String> key));
V8_WARN_UNUSED_RESULT MaybeLocal<Value> GetOwnPropertyDescriptor(
Local<Context> context, Local<String> key);
V8_DEPRECATE_SOON("Use maybe version", bool Has(Local<Value> key));
V8_WARN_UNUSED_RESULT Maybe<bool> Has(Local<Context> context,
Local<Value> key);
V8_DEPRECATE_SOON("Use maybe version", bool Delete(Local<Value> key));
// TODO(dcarney): mark V8_WARN_UNUSED_RESULT
Maybe<bool> Delete(Local<Context> context, Local<Value> key);
V8_DEPRECATED("Use maybe version", bool Has(uint32_t index));
V8_WARN_UNUSED_RESULT Maybe<bool> Has(Local<Context> context, uint32_t index);
V8_DEPRECATED("Use maybe version", bool Delete(uint32_t index));
// TODO(dcarney): mark V8_WARN_UNUSED_RESULT
Maybe<bool> Delete(Local<Context> context, uint32_t index);
V8_DEPRECATED("Use maybe version",
bool SetAccessor(Local<String> name,
AccessorGetterCallback getter,
AccessorSetterCallback setter = 0,
Local<Value> data = Local<Value>(),
AccessControl settings = DEFAULT,
PropertyAttribute attribute = None));
V8_DEPRECATED("Use maybe version",
bool SetAccessor(Local<Name> name,
AccessorNameGetterCallback getter,
AccessorNameSetterCallback setter = 0,
Local<Value> data = Local<Value>(),
AccessControl settings = DEFAULT,
PropertyAttribute attribute = None));
// TODO(dcarney): mark V8_WARN_UNUSED_RESULT
Maybe<bool> SetAccessor(Local<Context> context, Local<Name> name,
AccessorNameGetterCallback getter,
AccessorNameSetterCallback setter = 0,
MaybeLocal<Value> data = MaybeLocal<Value>(),
AccessControl settings = DEFAULT,
PropertyAttribute attribute = None);
void SetAccessorProperty(Local<Name> name, Local<Function> getter,
Local<Function> setter = Local<Function>(),
PropertyAttribute attribute = None,
AccessControl settings = DEFAULT);
Maybe<bool> HasPrivate(Local<Context> context, Local<Private> key);
Maybe<bool> SetPrivate(Local<Context> context, Local<Private> key,
Local<Value> value);
Maybe<bool> DeletePrivate(Local<Context> context, Local<Private> key);
MaybeLocal<Value> GetPrivate(Local<Context> context, Local<Private> key);
V8_DEPRECATE_SOON("Use maybe version", Local<Array> GetPropertyNames());
V8_WARN_UNUSED_RESULT MaybeLocal<Array> GetPropertyNames(
Local<Context> context);
V8_WARN_UNUSED_RESULT MaybeLocal<Array> GetPropertyNames(
Local<Context> context, KeyCollectionMode mode,
PropertyFilter property_filter, IndexFilter index_filter);
V8_DEPRECATE_SOON("Use maybe version", Local<Array> GetOwnPropertyNames());
V8_WARN_UNUSED_RESULT MaybeLocal<Array> GetOwnPropertyNames(
Local<Context> context);
V8_WARN_UNUSED_RESULT MaybeLocal<Array> GetOwnPropertyNames(
Local<Context> context, PropertyFilter filter);
Local<Value> GetPrototype();
V8_DEPRECATED("Use maybe version", bool SetPrototype(Local<Value> prototype));
V8_WARN_UNUSED_RESULT Maybe<bool> SetPrototype(Local<Context> context,
Local<Value> prototype);
Local<Object> FindInstanceInPrototypeChain(Local<FunctionTemplate> tmpl);
V8_DEPRECATED("Use maybe version", Local<String> ObjectProtoToString());
V8_WARN_UNUSED_RESULT MaybeLocal<String> ObjectProtoToString(
Local<Context> context);
Local<String> GetConstructorName();
Maybe<bool> SetIntegrityLevel(Local<Context> context, IntegrityLevel level);
int InternalFieldCount();
V8_INLINE static int InternalFieldCount(
const PersistentBase<Object>& object) {
return object.val_->InternalFieldCount();
}
V8_INLINE Local<Value> GetInternalField(int index);
void SetInternalField(int index, Local<Value> value);
V8_INLINE void* GetAlignedPointerFromInternalField(int index);
V8_INLINE static void* GetAlignedPointerFromInternalField(
const PersistentBase<Object>& object, int index) {
return object.val_->GetAlignedPointerFromInternalField(index);
}
void SetAlignedPointerInInternalField(int index, void* value);
void SetAlignedPointerInInternalFields(int argc, int indices[],
void* values[]);
// Testers for local properties.
V8_DEPRECATED("Use maybe version", bool HasOwnProperty(Local<String> key));
V8_WARN_UNUSED_RESULT Maybe<bool> HasOwnProperty(Local<Context> context,
Local<Name> key);
V8_WARN_UNUSED_RESULT Maybe<bool> HasOwnProperty(Local<Context> context,
uint32_t index);
V8_DEPRECATE_SOON("Use maybe version",
bool HasRealNamedProperty(Local<String> key));
V8_WARN_UNUSED_RESULT Maybe<bool> HasRealNamedProperty(Local<Context> context,
Local<Name> key);
V8_DEPRECATE_SOON("Use maybe version",
bool HasRealIndexedProperty(uint32_t index));
V8_WARN_UNUSED_RESULT Maybe<bool> HasRealIndexedProperty(
Local<Context> context, uint32_t index);
V8_DEPRECATE_SOON("Use maybe version",
bool HasRealNamedCallbackProperty(Local<String> key));
V8_WARN_UNUSED_RESULT Maybe<bool> HasRealNamedCallbackProperty(
Local<Context> context, Local<Name> key);
V8_DEPRECATED(
"Use maybe version",
Local<Value> GetRealNamedPropertyInPrototypeChain(Local<String> key));
V8_WARN_UNUSED_RESULT MaybeLocal<Value> GetRealNamedPropertyInPrototypeChain(
Local<Context> context, Local<Name> key);
V8_DEPRECATED(
"Use maybe version",
Maybe<PropertyAttribute> GetRealNamedPropertyAttributesInPrototypeChain(
Local<String> key));
V8_WARN_UNUSED_RESULT Maybe<PropertyAttribute>
GetRealNamedPropertyAttributesInPrototypeChain(Local<Context> context,
Local<Name> key);
V8_DEPRECATED("Use maybe version",
Local<Value> GetRealNamedProperty(Local<String> key));
V8_WARN_UNUSED_RESULT MaybeLocal<Value> GetRealNamedProperty(
Local<Context> context, Local<Name> key);
V8_DEPRECATED("Use maybe version",
Maybe<PropertyAttribute> GetRealNamedPropertyAttributes(
Local<String> key));
V8_WARN_UNUSED_RESULT Maybe<PropertyAttribute> GetRealNamedPropertyAttributes(
Local<Context> context, Local<Name> key);
bool HasNamedLookupInterceptor();
bool HasIndexedLookupInterceptor();
int GetIdentityHash();
// TODO(dcarney): take an isolate and optionally bail out?
Local<Object> Clone();
Local<Context> CreationContext();
bool IsCallable();
bool IsConstructor();
V8_DEPRECATED("Use maybe version",
Local<Value> CallAsFunction(Local<Value> recv, int argc,
Local<Value> argv[]));
V8_WARN_UNUSED_RESULT MaybeLocal<Value> CallAsFunction(Local<Context> context,
Local<Value> recv,
int argc,
Local<Value> argv[]);
V8_DEPRECATED("Use maybe version",
Local<Value> CallAsConstructor(int argc, Local<Value> argv[]));
V8_WARN_UNUSED_RESULT MaybeLocal<Value> CallAsConstructor(
Local<Context> context, int argc, Local<Value> argv[]);
V8_DEPRECATE_SOON("Keep track of isolate correctly", Isolate* GetIsolate());
static Local<Object> New(Isolate* isolate);
V8_INLINE static Object* Cast(Value* obj);
private:
Object();
static void CheckCast(Value* obj);
Local<Value> SlowGetInternalField(int index);
void* SlowGetAlignedPointerFromInternalField(int index);
};
來自:https://zhuanlan.zhihu.com/p/24982678