JavaScript对象属性如何在内存存储?新增属性是否会重分配内存?
JavaScript 对象属性存储机制解答
JS 引擎(以目前主流的 V8 为例)的对象存储逻辑和 C# 完全不同,你提到的「新增属性时迁移原对象内存位置」的情况不会发生。
核心存储结构
普通 JS 对象的底层分为三个独立的存储部分:
- 对象本体:固定大小的堆内存块,创建后地址永久不变,只存两个指针:一个指向隐藏类(也叫 Shape/Map),另一个指向属性值存储数组。
- 隐藏类:存储对象所有属性的元信息(比如属性名、属性值在存储数组里的偏移位置),作用是让属性查询不用每次遍历,能直接按偏移量快速取值,属于引擎的性能优化结构。
- 属性值存储数组:所有属性的实际值都存在这个数组里,有初始预留容量。
对应示例的执行逻辑
你给出的两行代码执行流程如下:
let obj = {}; // Line 1 obj.prop1 = 1; // Line 2
- Line 1 执行时:引擎在堆中创建固定大小的空对象本体,同时生成对应空结构的隐藏类,以及带初始预留容量的空属性值存储数组,对象本体里存入两个结构的指针后,地址就固定不会再修改。
- Line 2 新增
prop1属性时:- 引擎会基于原有隐藏类生成新的隐藏类,新增
prop1对应的偏移量信息,把对象本体的隐藏类指针指向新的隐藏类。 - 把数值 1 写入属性值存储数组的对应偏移位置即可。如果当前属性数量没有超出存储数组的预留容量,存储数组的位置也不会变;就算属性数量超了预留容量,引擎只会扩容迁移属性值存储数组,再把对象本体里的数组指针更新为新地址即可,对象本体的地址全程不变。
- 引擎会基于原有隐藏类生成新的隐藏类,新增
和 C# 逻辑的差异
C# 类的实例内存块会预分配所有预定义字段的存储空间,实例块本身大小是固定的,所以不支持动态加属性;C# 数组/List 扩容需要迁移整块内存,是因为它的内容直接存在连续的实例内存块里,逻辑和 JS 对象完全不同。
内容的提问来源于stack exchange,提问作者Jae Yi
相关产品推荐
相关产品推荐

