上一章我们讲了V8如何存储的对象,其中提到了隐藏类,这一章我们来看看隐藏类到底做了什么。
为什么要讲V8????
隐藏类
是V8引擎在运行时自动生成和管理的数据结构,用于跟踪对象的属性和方法
隐藏类的思想借鉴了静态语言的结构
function Person (name,age) {
this.name = name;
this.age = age;
}
let xiaoman = new Person('小满',24)
c++
#include <iostream>
#include <string>
struct Person
{
std::string name;
int age;
};
int main()
{
Person xiaoman;
xiaoman.age = 18;
xiaoman.name = "小满";
std::cout << "name: " << xiaoman.name << std::endl;
std::cout << "age: " << xiaoman.age << std::endl;
return 0;
}
首先在JavaScript运行时,例如xiaoman.age 去查询 age 上节课我们讲过 他会 通过快慢属性
去找 而且整个过程非常耗时。
而C++就不同了,C++在声明一个对象之前需要定义该对象的结构,c++ 代码在执行之前是需要被编译的,编译的时候对象都是固定的,也就是代码执行的时候 对象的形状是无法被改变的。
所以V8就引入了 隐藏类的概念
隐藏类 (Hiden Class)
隐藏类就是把JavaScript的对象也进行静态化,我们假设这个对象不会删除和新增
,这样形状就固定了
满足条件之后V8就会创建隐藏类,在这个隐藏类会创建对象的基础属性
在V8引擎中,每个隐藏类都有一个编号(map id
),用于唯一标识该隐藏类
举个例子,假设我们有以下两个对象:
let obj1 = { name: 1, age: 2 };
let obj2 = { name: 1, age: 2, address: 3 };
这两个对象具有相同的形状,即都有属性name
和age
,但obj2
还额外有一个属性address
。V8会为它们生成两个不同的隐藏类
// 隐藏类1:包含属性name和age
HiddenClass_1
├── map_id: 1
├── property_names: ['name', 'age']
├── transitions: {}
└── prototype: Object.prototype
// 隐藏类2:包含属性name、age和address
HiddenClass_2
├── map_id: 2
├── property_names: ['name', 'age', 'address']
├── transitions:
│ ├── a: HiddenClass_1
│ ├── b: HiddenClass_1
│ └── c: null
└── prototype: Object.prototype
可以看到,隐藏类1包含属性name
和age
,没有过渡表;而隐藏类2包含属性name
、age
和address
,其中属性name
和age
的过渡表指向隐藏类1,属性address
没有过渡表,表示该属性是新添加的
如果两个对象属性一样呢?
如果两个对象具有相同的属性,它们将共享同一个隐藏类。具体来说,当两个对象的属性顺序和类型都相同时,V8会为它们生成一个共享的隐藏类。
举个例子,假设我们有以下两个对象:
let obj1 = { name: 1, age: 2 };
let obj2 = { name: 1, age: 2 };
这两个对象具有相同的形状,即都有属性name
和age
,且属性的顺序和类型完全一致。V8会为它们生成一个共享的隐藏类,如下所示:
HiddenClass_1
├── map_id: 1
├── property_names: ['name', 'age']
├── transitions: {}
└── prototype: Object.prototype
可以看到,隐藏类1包含属性name
和age
,没有过渡表,而且两个对象都共享
这个隐藏类。
这种共享隐藏类的机制可以节省内存空间,因为不同的对象可以共享相同的隐藏类结构。