核心思路是将实例独有属性放构造函数内、共用方法挂载到prototype上,避免每次new都重复创建函数对象;因每个函数对象约72字节,大量实例会导致内存浪费和GC压力。

核心思路是:把实例独有数据放构造函数里,把所有实例共用的方法挂到 prototype 上。这样方法只在内存中存一份,避免重复创建函数对象。
为什么构造函数单独写方法会浪费内存
如果在构造函数内部定义方法,每次 new 一个实例,就会新建一个函数对象:
❌ 浪费内存的写法:
function Car(model) {
this.model = model;
this.drive = function() { // 每次 new 都新建一个函数
console.log(this.model + ' is driving');
};
}
const car1 = new Car('Tesla');
const car2 = new Car('BMW');
console.log(car1.drive === car2.drive); // false —— 两个不同函数对象每个函数对象本身占约 72 字节,若创建上万个实例,仅方法就额外占用数 MB 内存,还会增加垃圾回收(GC)压力。
立即学习“Java免费学习笔记(深入)”;
正确做法:构造函数 + prototype 分工
构造函数负责初始化实例属性,prototype 负责共享方法:
✅ 推荐写法:
function Car(model) {
this.model = model; // 实例独有 → 放构造函数
this.year = 2026; // 同理,基础数据放这里
}
<p>Car.prototype.drive = function() { // 所有实例共用 → 放 prototype
console.log(this.model + ' is driving');
};</p><p>Car.prototype.honk = function() {
console.log('Beep!');
};</p><p>const car1 = new Car('Tesla');
const car2 = new Car('BMW');
console.log(car1.drive === car2.drive); // true —— 指向同一函数此时 drive 和 honk 在内存中只有 1 份,无论创建多少个 Car 实例,都不再新增函数对象。
注意 constructor 属性的修复
如果重写整个 prototype(比如用对象字面量赋值),要手动恢复 constructor 指向,否则会丢失构造函数引用:
// ❌ 错误:重写 prototype 后 constructor 断了
Car.prototype = {
drive() { /* ... */ },
honk() { /* ... */ }
};
console.log(car1.constructor === Car); // false
<p>// ✅ 正确:显式补回 constructor
Car.prototype = {
constructor: Car, // 关键!
drive() { /<em> ... </em>/ },
honk() { /<em> ... </em>/ }
};不修复不影响功能,但会影响 instanceof 判断、序列化调试、某些库的类型识别等场景。
哪些该放 prototype,哪些不该放
-
适合放 prototype 的:不依赖闭包、不捕获局部变量、所有实例行为一致的方法(如
toString、save、validate) -
必须放构造函数的:需要绑定实例状态的私有数据(如
this._cache = new Map())、依赖闭包封装的私有逻辑 -
谨慎放 prototype 的:返回新对象或数组的方法(如
getOptions()),只要不修改内部状态,仍可共享;但如果方法内部用了new Date()或Math.random()等无副作用操作,也完全没问题


















