javascript原型链属性查找遵循自有属性优先原则:对象自身存在属性即停止查找,覆盖原型同名属性;删除后恢复原型访问;替换原型需用object.create保持委托;方法可覆盖、增强或隔离;getter可能被数据属性静默屏蔽。

原型链中的属性覆盖不是“需要处理”的问题,而是 JavaScript 的默认行为——只要对象自身有该属性,就不再查找原型。关键在于理解它怎么工作、什么时候该让它发生、什么时候该避免。
自有属性优先:遮蔽是硬性规则
每次访问 obj.prop,引擎第一步就是检查 obj.hasOwnProperty('prop')。只要返回 true,查找立刻停止,原型上的同名属性完全被忽略。
- 哪怕原型上定义了 Person.prototype.name = 'unknown',只要实例执行了 p.name = 'Alice',读取结果就是 'Alice'
- 删除后恢复:执行 delete p.name 后,再次访问 p.name 就会落到原型上,得到 'unknown'
- 方法、getter、Object.defineProperty 添加的属性,全部遵循同一规则——没有例外
覆盖原型对象本身:要小心断连
直接赋值替换整个原型(比如 Child.prototype = Parent.prototype 或 Child.prototype = {}),不是覆盖属性,而是切断继承链。这会导致:
- 子类实例无法再访问父类原型上的 getter/setter,因为委托目标消失了
- Child.prototype = Object.create(Parent.prototype) 才是安全做法,它保留委托关系
- 补上 constructor:设置 Child.prototype.constructor = Child
方法重名:覆盖、委托还是隔离?
同名方法在原型链上自然覆盖,但意图不同,做法也不同:
- 想完全替换:直接在子类 prototype 上定义同名方法,如 Child.prototype.render = function() { ... }
- 想增强逻辑:在子方法中用 Parent.prototype.method.call(this, ...) 或 Object.getPrototypeOf(Child.prototype).method.call(this, ...) 显式调用父逻辑
- 想共存不冲突:改用语义化命名,比如 initBasic 和 initWithAuth,避免强行同名
访问器属性被数据属性静默屏蔽
这是容易忽略的陷阱:父类用 getter 控制 fullName,子实例却执行 obj.fullName = 'A B':
- 如果父类没定义 setter,这次赋值会在实例上创建一个同名数据属性
- 从此 obj.fullName 不再触发 getter,变成普通字符串,行为突变
- 修复方式:删掉实例属性(delete obj.fullName),或确保父类提供合理 setter











