目錄
V8 Object 記憶體結構與屬性存取
Reference
Property Name:屬性名稱
In-Object Properties & Fast Property Access:物件內屬性與存取最佳化
Reused Hidden Class:重复使用的隐藏类
Methods & Prototypes:方法与原型
Dictionary Mode
Fast Elements:数值下标的属性
Object 代码声明
首頁 web前端 js教程 JavaScript V8 Object 記憶體結構與屬性存取詳解

JavaScript V8 Object 記憶體結構與屬性存取詳解

Mar 02, 2017 pm 03:27 PM
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

  • V8 Design Elements

  • A tour of V8: object representation

  • Demystifying v8 and JavaScript Performance

  • #V8 Docs :Object Class Reference

  • How does V8 manage the memory of object instances?

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的内存位置信息:

这里从C0C1的变化称为转换(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 代码声明

// 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&#39;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)!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

如何使用WebSocket和JavaScript實現線上語音辨識系統 如何使用WebSocket和JavaScript實現線上語音辨識系統 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript實現線上語音辨識系統引言:隨著科技的不斷發展,語音辨識技術已成為了人工智慧領域的重要組成部分。而基於WebSocket和JavaScript實現的線上語音辨識系統,具備了低延遲、即時性和跨平台的特點,成為了廣泛應用的解決方案。本文將介紹如何使用WebSocket和JavaScript來實現線上語音辨識系

WebSocket與JavaScript:實現即時監控系統的關鍵技術 WebSocket與JavaScript:實現即時監控系統的關鍵技術 Dec 17, 2023 pm 05:30 PM

WebSocket與JavaScript:實現即時監控系統的關鍵技術引言:隨著互聯網技術的快速發展,即時監控系統在各個領域中得到了廣泛的應用。而實現即時監控的關鍵技術之一就是WebSocket與JavaScript的結合使用。本文將介紹WebSocket與JavaScript在即時監控系統中的應用,並給出程式碼範例,詳細解釋其實作原理。一、WebSocket技

如何利用JavaScript和WebSocket實現即時線上點餐系統 如何利用JavaScript和WebSocket實現即時線上點餐系統 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket實現即時線上點餐系統介紹:隨著網路的普及和技術的進步,越來越多的餐廳開始提供線上點餐服務。為了實現即時線上點餐系統,我們可以利用JavaScript和WebSocket技術。 WebSocket是一種基於TCP協定的全雙工通訊協議,可實現客戶端與伺服器的即時雙向通訊。在即時線上點餐系統中,當使用者選擇菜餚並下訂單

如何使用WebSocket和JavaScript實現線上預約系統 如何使用WebSocket和JavaScript實現線上預約系統 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript實現線上預約系統在當今數位化的時代,越來越多的業務和服務都需要提供線上預約功能。而實現一個高效、即時的線上預約系統是至關重要的。本文將介紹如何使用WebSocket和JavaScript來實作一個線上預約系統,並提供具體的程式碼範例。一、什麼是WebSocketWebSocket是一種在單一TCP連線上進行全雙工

JavaScript與WebSocket:打造高效率的即時天氣預報系統 JavaScript與WebSocket:打造高效率的即時天氣預報系統 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的即時天氣預報系統引言:如今,天氣預報的準確性對於日常生活以及決策制定具有重要意義。隨著技術的發展,我們可以透過即時獲取天氣數據來提供更準確可靠的天氣預報。在本文中,我們將學習如何使用JavaScript和WebSocket技術,來建立一個高效的即時天氣預報系統。本文將透過具體的程式碼範例來展示實現的過程。 We

javascript如何使用insertBefore javascript如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用於在DOM樹中插入一個新的節點。這個方法需要兩個參數:要插入的新節點和參考節點(即新節點將要插入的位置的節點)。

簡易JavaScript教學:取得HTTP狀態碼的方法 簡易JavaScript教學:取得HTTP狀態碼的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教學:如何取得HTTP狀態碼,需要具體程式碼範例前言:在Web開發中,經常會涉及到與伺服器進行資料互動的場景。在與伺服器進行通訊時,我們經常需要取得傳回的HTTP狀態碼來判斷操作是否成功,並根據不同的狀態碼來進行對應的處理。本篇文章將教你如何使用JavaScript來取得HTTP狀態碼,並提供一些實用的程式碼範例。使用XMLHttpRequest

JavaScript與WebSocket:打造高效率的即時影像處理系統 JavaScript與WebSocket:打造高效率的即時影像處理系統 Dec 17, 2023 am 08:41 AM

JavaScript是一種廣泛應用於Web開發的程式語言,而WebSocket則是一種用於即時通訊的網路協定。結合二者的強大功能,我們可以打造一個高效率的即時影像處理系統。本文將介紹如何利用JavaScript和WebSocket來實作這個系統,並提供具體的程式碼範例。首先,我們需要明確指出即時影像處理系統的需求和目標。假設我們有一個攝影機設備,可以擷取即時的影像數

See all articles