1990년대 넷스케이프 브라우저가 출시되면서 자바스크립트가 처음으로 사람들의 눈에 들어왔습니다. 이후 AJAX의 대규모 애플리케이션이 등장하고 리치 클라이언트 및 단일 페이지 애플리케이션 시대가 도래하면서 JavaScript는 웹 개발에서 점점 더 중요한 위치를 차지하게 되었습니다. 초기 JavaScript 엔진에서는 웹 애플리케이션 개발 시 성능으로 인해 병목 현상이 점점 더 심해졌습니다. V8 엔진의 설계 목표는 대규모 JavaScript 애플리케이션의 실행 효율성을 보장하는 것입니다. 많은 테스트에서 V8의 성능이 JScript(Internet Explorer), SpiderMonkey(Firefox) 및 JavaScriptCore보다 우수하다는 것을 분명히 알 수 있습니다. (Safari) V8 소개의 공식 문서에 따르면 주로 속성 액세스, 동적 기계 코드 생성 및 효율적인 가비지 수집의 세 가지 측면에서 성능 최적화에 중점을 둡니다. 객체는 의심할 여지없이 JavaScript에서 가장 중요한 데이터 유형 중 하나입니다. 이 기사에서는 내부 구조를 자세히 설명합니다. 상속 다이어그램은 다음과 같습니다.
V8에서 새로 할당된 JavaScript 객체 구조는 다음과 같습니다.
[ class / map ] -> ... ; 指向内部类 [ properties ] -> [empty array] [ elements ] -> [empty array] ; 数值类型名称的属性 [ reserved #1 ] -\ [ reserved #2 ] | [ reserved #3 ] }- in object properties,即预分配的内存空间 ............... | [ reserved #N ] -/
새 객체를 생성할 때 V8은 소위 객체 내 속성을 저장하기 위한 사전 할당된 메모리 영역입니다. 사전 할당된 영역의 크기는 생성자(this.field = expr
)의 매개변수 수에 따라 결정됩니다. 객체에 새 속성을 추가하려고 할 때 V8은 먼저 이를 소위 순서대로 슬롯에 넣으려고 시도합니다. 객체 내 슬롯이 오버로드되면 V8은 새 속성을 out-order 슬롯에 추가하려고 시도합니다. 객체 속성 목록. 속성 이름과 속성 첨자 사이의 매핑 관계는 소위 히든 클래스에 저장됩니다. 예를 들어 { 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 ]
V8 Design Elements
V8: 객체 표현
v8과 JavaScript 성능 이해하기
V8 Docs: 객체 클래스 참조
V8은 객체 인스턴스의 메모리를 어떻게 관리하나요?
동적 언어인 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
은 현재 최대 첨자에 1을 더한 결과를 반환합니다(이때 문자열 첨자는 계산을 위해 숫자 유형으로 변환됩니다):
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
동적 Type 언어로서 JavaScript의 개체 속성은 런타임에 동적으로 추가 및 삭제될 수 있습니다. 즉, 전체 개체의 구조가 자주 변경된다는 의미입니다. 대부분의 JavaScript 엔진은 객체 속성을 저장하기 위해 사전 유형의 데이터 구조를 사용하는 경향이 있습니다. 속성에 액세스할 때마다 엔진은 내부 계층의 속성에 해당하는 첨자 주소를 동적으로 찾은 다음 값을 읽어야 합니다. 이 방법은 구현하기가 더 쉽지만 성능이 저하됩니다. 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
처음 구현에서는 새로운 객체가 설정될 때마다 object o
속성이 추가되면 V8은 새로운 숨겨진 내부 클래스(내부적으로 Map이라고 함)를 생성하여 속성 조회 속도를 최적화하기 위해 새 메모리 주소를 저장합니다. 두 번째 구현에서는 새 객체를 생성할 때 내부 클래스를 초기화하여 V8이 속성을 할당할 때 이러한 속성을 고성능으로 찾을 수 있도록 합니다. 세 번째 구현에서는 순수 V8에서 최고의 성능을 보이는 ES6 클래스를 사용합니다. 다음으로, 히든 클래스가 어떻게 작동하는지 자세히 설명하겠습니다.
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、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; }
由于普通的属性和数字式属性分开存放,即使数组退化为字典模式,也不会影响到其他属性的访问速度(反之亦然)。
// http://www.php.cn/ 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); };
以上就是JavaScript V8 Object 内存结构与属性访问详解的内容,更多相关内容请关注PHP中文网(www.php.cn)!