直接覆盖原型会切断继承链导致访问器失效;应使用Object.create(Parent.prototype)并补constructor;混用数据属性与访问器可能引发隐性屏蔽。

直接覆盖整个原型对象(比如 Child.prototype = Parent.prototype 或 Child.prototype = Object.create(null))会切断原本的继承链,导致子类实例无法再访问父类原型上定义的 getter/setter——这不是“遮蔽”,而是“断连”。访问器属性本身不复制、只委托,一旦原型被替换成一个不含这些访问器的新对象,委托就无处可去,自然彻底失效。
避免用赋值方式替换原型
用简单赋值覆盖 prototype 是最常见也最危险的操作:
-
Child.prototype = Parent.prototype:子类和父类共用同一原型,修改任一端都会影响另一端,且无法独立扩展 -
Child.prototype = {}或Child.prototype = Object.create(null):新原型没有constructor,也不包含任何父类方法或访问器,继承链完全断裂 - 正确做法是用
Object.create(Parent.prototype)创建干净的委托链,再补上constructor
确保访问器在新原型链中可委托
即使用了 Object.create(Parent.prototype),如果后续又用 Object.defineProperty 在子类原型上重定义同名属性,仍可能出问题:
- 若只定义了
setter而没配getter,读取时返回undefined(找不到 getter) - 若新定义的描述符中
configurable: false(默认值),后续无法在子类中重新定义该访问器 - 建议统一使用 ES6
class语法声明访问器,它默认configurable: true,更安全可控
识别并修复已失效的访问器调用
当发现某个本该触发逻辑的属性读写突然“静默”(比如不报错但也不执行 setter 中的日志或校验),可能是访问器已断连:
- 检查实例的
__proto__链:从obj.__proto__逐级向上,确认是否还能走到定义了该访问器的原型对象 - 手动测试委托:执行
Parent.prototype.hasOwnProperty('propName'),再查Child.prototype.hasOwnProperty('propName'),若前者为true后者为false,说明未被遮蔽,只是链断了 - 临时恢复方式:给
Child.prototype显式添加访问器,内部用super.propName调用(ES6 class 中)或通过Parent.prototype.xxx.call(this)手动委托(函数构造器中)
混用数据属性与访问器时的降级陷阱
这是失效的隐性变体:父类用访问器控制 fullName,子实例却执行 obj.fullName = 'A B':
- 若父访问器无
setter,这次赋值会在实例上创建一个同名**数据属性**,从此屏蔽原型 getter - 后续
obj.fullName不再走访问器逻辑,变成普通字符串,行为突变且难以察觉 - 防御方式:在父类 setter 中做类型/格式校验;或用
Object.defineProperty将原型上的访问器设为writable: false+configurable: false(慎用),阻止实例意外写入

















