목차
V8 객체 메모리 구조와 속성 접근
참조
속성 이름: 속성 이름
객체 내 속성 및 빠른 속성 액세스: 객체 내 속성 및 액세스 최적화
Reused Hidden Class:重复使用的隐藏类
Methods & Prototypes:方法与原型
Dictionary Mode
Fast Elements:数值下标的属性
Object 代码声明
웹 프론트엔드 JS 튜토리얼 JavaScript V8 객체 메모리 구조 및 속성 액세스에 대한 자세한 설명

JavaScript V8 객체 메모리 구조 및 속성 액세스에 대한 자세한 설명

Mar 02, 2017 pm 03:27 PM
javascript

V8 객체 메모리 구조와 속성 접근

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
로그인 후 복사

Functionlength 속성이 매개변수의 길이를 반환한다는 점을 제외하면 본질적으로 객체이기도 합니다.

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

这里从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 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌
Will R.E.P.O. 크로스 플레이가 있습니까?
1 몇 달 전 By 尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

메모장++7.3.1

메모장++7.3.1

사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전

SublimeText3 중국어 버전

중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경

드림위버 CS6

드림위버 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의 적용을 소개하고 코드 예제를 제공하며 구현 원칙을 자세히 설명합니다. 1. 웹소켓 기술

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를 사용하여 온라인 예약 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. WebSocket이란 무엇입니까? WebSocket은 단일 TCP 연결의 전이중 방식입니다.

JavaScript와 WebSocket: 효율적인 실시간 일기예보 시스템 구축 JavaScript와 WebSocket: 효율적인 실시간 일기예보 시스템 구축 Dec 17, 2023 pm 05:13 PM

JavaScript 및 WebSocket: 효율적인 실시간 일기 예보 시스템 구축 소개: 오늘날 일기 예보의 정확성은 일상 생활과 의사 결정에 매우 중요합니다. 기술이 발전함에 따라 우리는 날씨 데이터를 실시간으로 획득함으로써 보다 정확하고 신뢰할 수 있는 일기예보를 제공할 수 있습니다. 이 기사에서는 JavaScript 및 WebSocket 기술을 사용하여 효율적인 실시간 일기 예보 시스템을 구축하는 방법을 알아봅니다. 이 문서에서는 특정 코드 예제를 통해 구현 프로세스를 보여줍니다. 우리

간단한 JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법 간단한 JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법 Jan 05, 2024 pm 06:08 PM

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

자바스크립트에서 insertBefore를 사용하는 방법 자바스크립트에서 insertBefore를 사용하는 방법 Nov 24, 2023 am 11:56 AM

사용법: JavaScript에서 insertBefore() 메서드는 DOM 트리에 새 노드를 삽입하는 데 사용됩니다. 이 방법에는 삽입할 새 노드와 참조 노드(즉, 새 노드가 삽입될 노드)라는 두 가지 매개 변수가 필요합니다.

JavaScript 및 WebSocket: 효율적인 실시간 이미지 처리 시스템 구축 JavaScript 및 WebSocket: 효율적인 실시간 이미지 처리 시스템 구축 Dec 17, 2023 am 08:41 AM

JavaScript는 웹 개발에 널리 사용되는 프로그래밍 언어인 반면 WebSocket은 실시간 통신에 사용되는 네트워크 프로토콜입니다. 두 가지의 강력한 기능을 결합하면 효율적인 실시간 영상 처리 시스템을 만들 수 있습니다. 이 기사에서는 JavaScript와 WebSocket을 사용하여 이 시스템을 구현하는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 첫째, 실시간 영상처리 시스템의 요구사항과 목표를 명확히 할 필요가 있다. 실시간 이미지 데이터를 수집할 수 있는 카메라 장치가 있다고 가정해 보겠습니다.

See all articles