原型链不管理属性描述符,但影响属性访问与设置行为;描述符仅作用于自有属性,不继承,屏蔽现象导致赋值时在当前对象新建属性,accessor属性可避免屏蔽,object.create(null)可消除原型链干扰。

原型链本身不直接参与属性描述符(如 writable、configurable、enumerable)的管理,但它深刻影响属性访问与设置的行为逻辑——而属性描述符正是定义这些行为的核心机制。理解二者如何协同,才能真正掌握 JS 对象的底层控制逻辑。
属性描述符只作用于自有属性,不随原型链继承
当你用 Object.defineProperty() 在某个对象上定义一个属性并设置其描述符,这个描述符仅对该对象自身的该属性生效。即使该属性后来被原型链上的其他对象“继承”访问,继承来的只是属性值或方法,而不是它的描述符配置。
- 例如:
obj.a是自有属性且writable: false,那么修改obj.a会失败(严格模式报错);但若obj.a不存在,而obj.__proto__.a存在且可写,此时obj.a = 123实际会在obj上新建一个自有属性a(触发 [[Put]] 操作),而非修改原型上的a。 -
configurable: false的属性无法被delete,也无法被defineProperty重新配置——这个限制只对定义它的那个对象起效,不影响原型链上同名属性的可配置性。
原型链决定属性查找路径,但不改变描述符语义
JS 引擎在读取属性时走原型链([[Get]]),在设置属性时默认只操作当前对象([[Put]]),除非属性是 accessor(getter/setter)类型。
- 如果
obj.x未定义,而obj.__proto__.x是一个get描述符定义的 accessor 属性,那么访问obj.x实际执行的是原型上 getter 的逻辑——这时描述符和原型链共同协作完成行为。 - 但如果
obj.__proto__.x是一个writable: false的数据属性,你对obj.x赋值不会报错(因为是在obj上新建),也不会影响原型上x的不可写性。
屏蔽(Shadowing)是原型链与描述符交互的关键现象
当给一个继承自原型的属性赋值时,若该属性在原型上是数据属性(非 accessor),且当前对象没有同名自有属性,JS 会自动在当前对象上创建一个新属性——这叫“屏蔽”。此时,新属性的描述符默认为 writable: true, enumerable: true, configurable: true,与原型上原属性的描述符无关。
- 这意味着:即使
Parent.prototype.name是configurable: false,子实例child.name = 'new'仍会成功,并在child上创建一个全新的、可配置的name。 - 只有 accessor 属性能避免屏蔽——因为设置时会触发 setter,而非新建属性。
创建无原型干扰的对象可绕过描述符继承副作用
若需完全隔离原型链对属性操作的影响(比如实现纯粹的字典式对象),可用 Object.create(null) 创建无原型的对象:
- 它没有
__proto__,不继承toString、hasOwnProperty等任何方法,自然也不存在原型链上的属性查找或屏蔽问题。 - 此时所有属性均为自有属性,所有描述符约束都按定义严格生效,适合做配置对象、Map 替代、或需要精确控制属性行为的场景。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











