CommonJS模块应导出构造函数而非实例,通过修改其prototype实现对所有实例的动态方法注入;直接操作实例或__proto__不可靠且违背原型链设计意图。

CommonJS 模块导出的是一个值的拷贝,而原型链是运行时对象间动态关联的机制。两者本身处于不同层级:CommonJS 管模块导出/导入的时机和封装边界,原型链管对象属性查找与共享行为。因此,“向导出的构造函数实例动态注入方法”,**不能也不该直接操作实例本身**(那只是临时对象),而应通过修改其**原型(prototype)** 来实现对所有未来及已有实例的统一增强——这才是符合原型链设计意图、真正“动态生效”的方式。
确保导出的是构造函数而非实例
CommonJS 模块必须导出构造函数(即函数对象),才能让使用者用 new 创建实例,并参与原型链。若导出的是实例(如 module.exports = new Foo()),就失去了原型扩展能力。
- ✅ 正确导出:
- ❌ 错误导出(无法后续加方法到原型):
在模块内部扩展 prototype(推荐)
最清晰、可维护的方式是在定义构造函数的同一模块中,直接在其 prototype 上添加方法。这样所有通过 new 创建的实例都能立即继承。
- 在
user.js中:
User.prototype.sayHello = function() { return `Hello, ${this.name}`; };
module.exports = User;
- 使用方无需任何额外操作,实例天然拥有
sayHello:
const u = new User('Bob');
console.log(u.sayHello()); // "Hello, Bob"
在其他模块中动态补丁 prototype(谨慎使用)
若因架构约束需在外部模块为已导出的构造函数“打补丁”,可直接修改其 prototype。这会实时影响所有已存在和将创建的实例,但属于运行时副作用,应明确注释并避免重复执行。
立即学习“Java免费学习笔记(深入)”;
- 在
enhance.js中:
// 动态注入方法
User.prototype.toJSON = function() { return { name: this.name }; };
// 注意:不要重复赋值 prototype,只增补方法
- 关键点:
✔ 修改的是 User.prototype,不是某个实例的 __proto__;
✔ 不依赖 __proto__(非标准且不可靠);
✔ 所有 new User() 实例立刻可用新方法。
不推荐的做法:操作实例或 __proto__
以下方式违背原型链本意,且不可靠:
- 给单个实例加方法:
u.sayHi = () => 'Hi'→ 只作用于该实例,不复用; - 改实例的
__proto__:u.__proto__.sayHi = ...→ 非标准 API,部分环境不支持,且语义混乱; - 重写整个
prototype:User.prototype = { ... }→ 会丢失原有方法(如constructor),破坏 instanceof 判断。
原型链的威力在于共享与委托,不是修补单个对象。CommonJS 提供了模块封装,而 prototype 提供了运行时扩展能力——二者配合的关键,是始终面向构造函数及其 prototype 编程。


















