原型上不能放引用类型值,因为prototype被所有实例共享,导致修改一个实例的引用属性(如数组、对象)会意外影响其他实例;正确做法是将引用类型在构造函数中用this初始化,确保每个实例拥有独立副本。

直接在构造函数的 prototype 上挂载数组、对象等引用类型属性,会导致所有实例共享同一份数据——这是原型链中最隐蔽也最危险的陷阱之一。
为什么原型上不能放引用类型值
因为 prototype 是被所有实例共享的对象。一旦你在上面写 Person.prototype.skills = [],这个空数组就只有一份,所有 new Person() 创建的实例都会通过原型链访问并修改它。
例如:
❌ 错误示范:
立即学习“Java免费学习笔记(深入)”;
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
function Person(name) { this.name = name; }<br>Person.prototype.hobbies = []; // 所有实例共用一个数组<br>const a = new Person('Alice');<br>const b = new Person('Bob');<br>a.hobbies.push('reading');<br>console.log(b.hobbies); // ['reading'] —— Bob 也被影响了
正确做法:把引用类型放在实例上
每个实例需要独立副本的数据,必须在构造函数内部用 this 初始化,确保每次 new 都新建一份:
-
this.hobbies = [];✅ 每个实例都有自己的数组 -
this.config = { theme: 'light' };✅ 每个实例拥有独立配置对象 - 避免写
Person.prototype.hobbies = []或Person.prototype.defaults = {}
如果真需要共享默认结构,用工厂函数兜底
某些场景下你确实想提供“默认模板”,但又不能让实例直接改它。可以封装成返回新副本的函数:
Person.prototype.getDefaultConfig = () => ({ theme: 'light', lang: 'zh' });- 实例调用时得到的是全新对象:
this.config = this.getDefaultConfig(); - 或者更稳妥地写成静态方法:
Person.getDefaultConfig = () => ({ ... });
检查与防护小技巧
开发中可加一层防御性判断,防止意外修改原型上的引用值:
- 用
Object.freeze({})或Object.seal([])冻结原型上的基础配置(仅限只读场景) - 在方法里主动检测:
if (Array.isArray(this.hobbies)) { ... },避免因原型污染导致类型错乱 - ESLint 规则
no-prototype-builtins和自定义规则可拦截prototype.xxx = [...]类赋值

















