原型链继承中引用类型属性被共享是机制使然,解决核心是让每个实例拥有独立引用副本:在构造函数中初始化、用类字段语法、getter返回新对象,或组合继承时用Object.create设置原型并调用Parent.call。

原型链继承中引用类型属性被共享,不是代码写错,而是机制使然:所有实例通过 __proto__ 指向同一个 prototype 对象,而数组、对象、Map 等引用类型赋值时只复制地址——改一个,全跟着变。解决的核心思路只有一个:让每个实例拥有自己的引用副本,而不是共用“模板”。
在构造函数里初始化引用属性
每次 new 都会执行构造函数,天然隔离数据。这是最直接、兼容性最好的方式。
- 父类中把引用属性写在构造函数内,而非挂到
prototype上 - 子类继承时,必须在子构造函数中调用
Parent.call(this),确保父类的初始化逻辑运行在当前实例上 - 示例:
function Animal() { this.tags = []; this.meta = {}; }—— 每个实例都有独立的tags数组
用类字段语法(推荐 ES6+)
类字段(class fields)语义清晰,自动绑定到实例,无需手动调用 call,也避免了 prototype 上的污染。
- 直接在类体内声明:
class Dog { items = []; config = { mode: 'normal' }; - 它等价于在构造函数中赋值,但更简洁、可读性更强
- 注意不要写成
static items = [],那仍是共享的
用 getter 返回新对象
适合默认配置、懒加载或不可变场景。每次访问都生成全新引用,彻底隔绝副作用。
立即学习“Java免费学习笔记(深入)”;
- 定义为
get defaults() { return { timeout: 3000, retries: 2 }; } - 调用
dog.defaults每次返回不同对象,修改它不会影响其他实例 - 不适用于需要长期持有并多次修改的属性(比如缓存列表),因为每次取都是新对象
组合继承时注意原型设置方式
单纯用 Child.prototype = new Parent() 会把父类构造函数执行一次,其返回对象成为子类原型——里面的引用属性就固化在原型上了。
- 正确做法:用
Object.create(Parent.prototype)设置原型,避免执行父构造函数 - 再手动修正
constructor:Child.prototype.constructor = Child - 最后在子构造函数中调用
Parent.call(this, ...args),完成实例属性初始化


















