原型继承中引用类型属性共享是机制而非bug,须在构造函数或类字段中初始化确保实例独立;getter适合只读默认值;子类需借用构造函数继承避免共享;可用push+console或object.getprototypeof对比验证。

原型继承中引用类型属性共享不是 bug,而是机制本身——所有实例通过原型链访问同一个数组或对象,改一个就全变。关键不是“避免原型”,而是确保每个实例拿到的是独立副本。
在构造函数里初始化引用属性
每次 new 都执行一次,天然隔离:
- 函数写法:在构造函数内部赋值,比如 this.items = [] 或 this.config = { timeout: 5000 }
- 类写法:用类字段语法(ES6+),如 items = [],它自动绑定到实例,不走 prototype
- 别写成 Animal.prototype.items = [],这是共享的根源
用 getter 返回新引用(适合只读场景)
每次访问都生成新对象/数组,彻底隔绝副作用:
- 例如:get defaults() { return { lang: 'zh', theme: 'light' }; }
- 适合配置项、默认值、计算型数据
- 不适合需长期持有并多次修改的引用类型(比如缓存 Map)
借用构造函数继承子类引用属性
当涉及父子类继承且父类有引用属性时,仅靠原型链会出问题:
- 在子类构造函数中调用 Parent.call(this, args),让父类逻辑在子类实例上执行
- 这样 this.colors 等引用属性就落在子类实例自身,而非共用的父类原型
- 搭配 Object.create(Parent.prototype) 设置子类原型,兼顾方法复用和属性隔离(即寄生组合继承)
快速验证是否已踩坑
两行代码就能暴露隐患:
- 改一个实例,再读另一个:a.data.push(1); console.log(b.data) —— 若 b 也变了,说明共享
- 查内存地址:Object.getPrototypeOf(a).data === Object.getPrototypeOf(b).data 返回 true 就是同一份
- 团队审查时盯住 prototype.xxx = [] / {} / new Map() 这类赋值语句











