上一章我们讲了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
,没有过渡表,而且两个对象都共享
这个隐藏类。
这种共享隐藏类的机制可以节省内存空间,因为不同的对象可以共享相同的隐藏类结构。