子类原型被整体替换后,已有实例无法访问后续新增方法,因实例的[[Prototype]]在创建时已固定指向当时的prototype对象,后续替换只影响新实例。

子类原型被整体替换后,已有实例无法访问后续新增的方法,这是 JavaScript 原型继承中一个典型但容易被忽略的“时序陷阱”。根本原因在于:实例的 __proto__ 在创建时就固定指向当时的 prototype 对象;一旦你用 Son.prototype = new Father() 替换了整个原型对象,之前已创建的实例仍连着旧的(或未定义的)原型,不会自动更新。
关键问题:实例与原型的绑定是“一次性”的
每个实例在 new Son() 执行时,其内部 [[Prototype]] 会立即指向**当时**的 Son.prototype。之后你再给 Son.prototype 赋新值,只影响未来的新实例,对已存在的实例完全无效。
- 比如:
const s1 = new Son()→ s1.__proto__ 指向“旧的” Son.prototype - 接着执行:
Son.prototype = new Father()→ 这只是让 Son 构造函数的 prototype 属性指向新对象 - 再定义:
Son.prototype.sayHi = function(){}→ 新方法只存在于新原型上 - 此时调用
s1.sayHi()就是undefined,因为 s1 的 __proto__ 并不等于这个新原型
正确写法:方法定义必须在原型赋值之后
所有自定义方法(包括子类特有方法)必须写在 Son.prototype = new Father() 之后,确保它们落在最终生效的那个原型对象上。
- ✅ 正确顺序:
Son.prototype = new Father();-
Son.prototype.constructor = Son;(修复 constructor 指向) Son.prototype.Sontype = function() { console.log('Son-------'); };- ❌ 错误顺序(常见初学者写法):
-
Son.prototype.Sontype = ...;(此时 Son.prototype 还是默认空对象) -
Son.prototype = new Father();(覆盖掉前面定义的方法)
更健壮的做法:用 Object.create 避免污染父类实例
直接 new Father() 作为子类原型,会把父类构造函数中的实例属性(如 this.colors = [])也带进来,造成所有子实例共享引用类型——这本身就有隐患。推荐用 Object.create(Father.prototype) 显式继承原型链,再手动挂载子类方法。
Son.prototype = Object.create(Father.prototype);Son.prototype.constructor = Son;Son.prototype.Sontype = function() { ... };- 这样既避免了父类实例属性的意外共享,又保证了方法定义在正确的原型对象上
已有实例补救?通常不建议也不可行
已创建的实例无法“动态切换”原型链。虽然技术上可通过 Object.setPrototypeOf(s1, Son.prototype) 强制修改,但:
- 该操作性能差,且在严格模式或某些环境(如 React Native 旧版)中可能被禁用
- 违背封装原则,容易引发不可预测行为
- 真实项目中应从设计阶段规避:确保原型设置和方法定义完成后再创建任何实例

















