V8 Object 內存結構與屬性訪問詳解從屬於筆者的Web 前端入門與工程實踐,推薦閱讀2016-個人前端之路:工具化與工程化。更多關於 JavaScript 引擎文章參考這裏。html
上世紀九十年代,隨着網景瀏覽器的發行,JavaScript 首次進入人們的視線。以後隨着 AJAX 的大規模應用與富客戶端、單頁應用時代的到來,JavaScript 在 Web 開發中佔據了愈來愈重要的地位。在早期的 JavaScript 引擎中,性能愈加成爲了開發網頁應用的瓶頸。而 V8 引擎設計的目標就是爲了保證大型 JavaScript 應用的執行效率,在不少測試中能夠明顯發現 V8 的性能優於 JScript (Internet Explorer), SpiderMonkey (Firefox), 以及 JavaScriptCore(Safari). 根據 V8 的官方文檔介紹,其主要是從屬性訪問、動態機器碼生成以及高效的垃圾回收這三個方面着手性能優化。Obejct 當屬 JavaScript 最重要的數據類型之一,本文咱們對其內部結構進行詳細闡述。其繼承關係圖以下所示:前端
在 V8 中新分配的 JavaScript 對象結構以下所示:node
[ 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}
對象的存儲方式可能以下:git
[ 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 會轉回到傳統的字典模式/哈希表模式:github
[ class ] -> [ OBJECT IS IN DICTIONARY MODE ] [ properties ] -> [a: 1, b: 2, c: 3, d: 4, e: 5] ; this is classical hash table [ elements ]
做爲動態語言,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
做爲動態類型語言,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
咱們在上文中說起,若是每次添加新的屬性時都建立新的隱藏類無疑是極大的性能浪費,實際上當咱們再次建立新的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; } }
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 描述符,而調用原型方法每每會比調用自身方法慢一點,畢竟引擎不只要去掃描自身的隱藏類,還要去掃描原型鏈上對象的隱藏類才能得知真正的函數調用地址。不過這個不會對於代碼的性能形成明顯的影響,所以寫代碼的時候也沒必要當心翼翼的避免這個。
對於複雜屬性的對象,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 儘量地會避免使用這種存儲方式。
V8 中將屬性名爲非負整數(0、一、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; }
因爲普通的屬性和數字式屬性分開存放,即便數組退化爲字典模式,也不會影響到其餘屬性的訪問速度(反之亦然)。
// 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); };