应将引用类型属性在构造函数中用this初始化,原型上仅放方法;子类继承时用Object.create(Parent.prototype)设原型并调用Parent.call(this)确保实例独立。

原型继承中多个实例共享引用类型,本质是因为这些引用值被放在了原型对象上,而所有实例都通过 __proto__ 指向同一个原型。要避免修改一个实例影响其他实例,关键在于:**让每个实例拥有自己独立的引用类型副本,而不是共用原型上的那一份**。
把引用类型属性移到构造函数中初始化
这是最直接、最常用且推荐的做法。原型上只放方法(函数),所有需要独立状态的数据(尤其是数组、对象、日期等)都在构造函数里用 this 创建。
- 父类中,
this.arr = [1, 2]放在构造函数内,而非写在Parent.prototype.arr = [1, 2] - 子类继承时,通过
Parent.call(this, ...)调用父类构造函数,确保每个子类实例都获得一份新的数组 - 这样即使
child1.arr.push(3),child2.arr完全不受影响
慎用原型链继承父类实例作为子类原型
像 Child.prototype = new Parent() 这种写法,会把父类构造函数中创建的引用类型(如 this.colors = ['red'])变成子类所有实例共享的“原型属性”。一旦其中一个实例修改该数组,其他实例立刻可见。
- 如果必须用原型链继承方法,应配合构造函数调用,即采用组合式继承
- 避免单独使用
new Parent()赋值给子类prototype,尤其当父类构造函数内部有引用类型初始化时 - 现代写法更倾向用
Object.create(Parent.prototype)建立原型关系,再手动挂载子类自己的实例属性
用 Object.create 搭配 call 实现干净的组合继承
这是目前最稳妥、被《JavaScript高级程序设计》推荐的方案,兼顾方法复用与实例隔离。
立即学习“Java免费学习笔记(深入)”;
- 子类构造函数中执行
Parent.call(this, name)→ 确保每个实例拥有独立的this.name、this.list等 - 子类原型设为
Object.create(Parent.prototype)→ 继承父类原型方法,但不执行父类构造函数(避免重复初始化或污染原型) - 手动修复
Child.prototype.constructor = Child,保持构造器指向正确
警惕 for...in 和原型上的可枚举引用属性
即使你没主动改数组,某些遍历操作(如 for...in)可能意外触发对原型上引用属性的读写。若原型上定义了 arr: [] 且是可枚举的,所有实例都会暴露它。
- 尽量不在原型上定义任何引用类型值(包括空数组
[]、空对象{}) - 如确需默认值,应在构造函数中按需创建,而不是提前挂在原型上
- 可通过
Object.defineProperty将原型属性设为不可枚举,但这不能解决共享问题,只是隐藏


















