JS对象在 V8 内的表达
先划重点:V8 中具有相同构建结构的 JSObject 对象,具有相同的内存(空间)布局。
JavaScript 对象会在堆上(根据需求)分配恒定大小的空间:
- 预分配(不超过)一定大小*的空间用作对象内属性存储(inobject_properties)。
- 预分配空间不足时(无空闲 slot),新增属性会存储在 properties 内。
- 数字式属性存储在 elements 内。
- properties/elements 空间不足时会创建(拷贝)一个更大的 FixedArray。

- V8 定义 JSObject 最大实例大小为 kMaxInstanceSize = 2040 byte,
其中:
kPointerSize = sizeof(void ) = 8 byte(64 位平台),
kHeaderSize = kPointerSize 3,
所以:max(inobject_properties) = 252 个
为了便于说明,我们来看个例子:
1、新建一个对象
var obj = {};

通过对象字面量创建的无属性对象分配 4 个对象内属性存储(inobject_properties)空间。
2、添加 3 个属性
obj.x = 1;
obj.y = 2;
obj.z = 3;

属性(“x”, “y”, “z”)优先存储在对象内属性存储中。
3、添加 2 个数字式属性
obj[0] = "a";
obj[1] = "b";

数字式属性(“0″, “1”)存储在 elements 内。
4、继续添加 3 个属性
obj.a = "a";
obj.b = "b";
obj.c = "c";

优先使用对象内属性存储(“a”),预分配空间不足时,属性(“b”, “c”)会存储在 properties 内。
看到这里问题来了,V8 是如何知道空间分配与空间结构的状况了?
这就要提起
我们以 JSObject

- type: 表述了堆内实例是一个 JSObject 对象
- inobject properties:对象内存储空间(包含未使用的 slots)
- unused property fields:未使用的属性存储空间
- instance size:实例(在堆内)的大小
- constructor:对象构造器
- prototype:对象原型
- stable[dictionary]:对象当前状态
- stable_map:快速模式
- dictionary_map:字典模式
- … …
当对象处于字典模式时直接通过 Jenkins hash 则可得到属性值存取的位置,对象处于快速模式时 V8 采用 JSObject

除此之外,处于快速模式的对象 V8 还结合 JSObject
为了便于理解 Transitions,我们来举个例子:
在实例化构造函数时,根据 ECMA-262 标准,新建一个空对象:

在添加属性 x = 1 时:
- 生成新的
(#1) 并将 JSObject 的引用改为新创建的 (#1)。 - 向原先引用的
(#0) 的 transitions 中写入一个 transition #x to Map(#1)。 - 在当前 instance_descriptors(own) 写入 #x in offset 0。
- 将数字 1(Smi) 写入 JSObject 对象内属性存储的第 0 位(根据 instance_descriptors 中的标识)。
形成如下图结构:

继续添加属性 y = 2 时,执行过程与添加属性 x 类似,最终生成的对象 p1 如下图结构:

一般来说通过对象的构建过程,最终会形成 TransitionsList 甚至是 TransitionsTree。
但是看上去在对象 p1 实例化过程中各个
我们再次实例化构造函数,但这回传入的参数为两个字符串(“a”, “b”):

在添加属性 x = “a” 时:
- 在 JSObject
(#0) 的 transitions 中根据 key=x 查找 transition 记录,并按图索骥将 JSObject 的引用改为发现的 (#1) (在这里并不会再新建 )。 - 读取当前的 instance_descriptors 查找到 #x 的存储位置 offset 0。
- 将字符串 “a” (Pointer)写入 JSObject 对象内属性存储的第 0 位(根据 instance_descriptors 中的标识)。
需要注意的是:这里的 DescriptorArray 实例(也就是 instance_descriptors )为
(#2) 所有,并非 (#1)(#0) ,故在这里 instance_descriptors 只能读取不可写入 。
形成如下图结构:

继续添加属性 y = “b” 时,执行过程与添加属性 x 类似,最终生成的对象 p2 如下图结构:

在这里我们发现,虽然 p1与 p2 存储的实际数据(类型)不同,但是通过 Transitions 特性它们共享相同的一个

我们总结下,一般来说由相同顺序、相同属性名(同一构造函数)构建的对象,共享同一个
JSObject
也被称为 Hidden Class ,这是由于最早 V8 在 Design Elements 将其称之为 Hidden Class,故一直沿用至今。
也就说,在 V8 中具有相同构建结构的 JSObject 对象,在堆内具有相同的内存(空间)布局。
而相同的内存(空间)布局是 V8 优化对象存取的基础。