属性描述符不直接存储,而是编译为对象内部状态标记并映射到属性字典或隐藏类中;引擎据此控制属性行为、优化访问性能,并在访问器属性或configurable/enumerable受限时触发相应机制。

属性描述符本身不直接“存储”在引擎内部,而是作为元信息参与对象属性的定义与行为控制。JavaScript 引擎(如 V8、SpiderMonkey)在底层并不持久保存完整的描述符对象,而是将描述符中各字段(configurable、enumerable、writable、value、get、set)编译为对象属性的内部状态标记,并映射到对象的属性字典(property dictionary)或隐藏类(hidden class)结构中。
属性字典中记录描述符特性
每个对象拥有独立的属性字典,用于存放自有属性。当调用 Object.defineProperty(obj, 'x', { writable: false, configurable: true }) 时,引擎会:
- 将 writable: false 转为该属性在字典中的只读标记(例如 V8 中的
kReadOnlyflag) - 把 configurable: true 编码为允许后续修改描述符或删除该属性的权限位
- 若含 get/set,则不存 value,而是将访问器函数指针挂载到该条目,并标记为“访问器属性”类型
-
enumerable: false 会影响该属性是否出现在
for...in或Object.keys()的结果中,引擎在枚举时跳过带此标记的条目
隐藏类(Hidden Class)与属性结构优化
引擎为提升属性访问性能,会对具有相同属性名、相同描述符特性的对象归类到同一隐藏类。例如:
const a = {}; Object.defineProperty(a, 'id', { value: 1, writable: false });const b = {}; Object.defineProperty(b, 'id', { value: 2, writable: false });
这两个对象可能共享同一个隐藏类,因为它们都拥有一个不可写的自有数据属性 id。此时,引擎可生成内联缓存(IC),直接通过偏移量读取 id 字段,无需查字典。
一旦描述符被修改(如把 writable 从 false 改为 true),引擎通常会触发隐藏类迁移(class transition),创建新隐藏类并复制属性,以维持结构一致性。
访问器属性触发运行时钩子
当描述符含 get 或 set 时,引擎不会在属性字典中存实际值,而是在访问时动态调用对应函数:
- 读取属性时,引擎检测到是访问器类型,立即调用
get函数并返回其返回值 - 赋值时,引擎跳过写入检查,直接执行
set函数体,参数为右侧表达式的求值结果 - Vue 2 的响应式系统正是基于此机制:对 data 对象每个属性重定义为带 get/set 的访问器,从而拦截依赖收集与派发更新
configurable 和 enumerable 是运行时约束标志
这两项不改变内存布局,但影响引擎对操作的合法性判断:
-
configurable: false → 后续无法再调用
Object.defineProperty修改该属性的任何描述符字段,也无法用delete删除它 -
enumerable: false → 不影响属性存在性或可访问性(
obj.prop仍能读),仅控制其是否出现在枚举上下文中 - 这些限制由引擎在每次相关操作时实时校验,失败则抛出
TypeError(严格模式下)或静默忽略(非严格模式下)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











