构造函数中返回新对象会破坏原型链,导致instanceof失效、原型方法不可用、constructor丢失;es6 class已禁止此行为,应改用静态工厂方法并严格校验继承完整性。

构造函数里返回一个全新对象,会直接切断原型链,让实例不再是该类的真正实例——这不是小问题,而是继承体系崩塌的起点。这种写法在 JavaScript 中虽语法允许,但后果严重:instanceof 失效、原型方法不可用、constructor 属性丢失、继承链形同虚设。
明确区分构造函数与工厂函数
构造函数职责唯一:初始化 this。它不该“产出”对象,只负责“塑造”当前实例。一旦你写了 return { ... } 或 return new Date(),你就已经越界成了工厂函数。
- 把带逻辑的对象创建封装成静态方法,比如
User.createWithValidation(),而不是塞进function User() { ... return {...} } - 工厂函数返回的对象不参与原型继承,也不应被当作子类实例使用;若需多态,必须确保返回的是真实构造出来的派生类实例
- 团队代码审查时,可将“构造函数中出现 return + 对象字面量/引用类型”列为硬性禁用项
严格禁止在 class 构造器中 return 对象
ES6 class 语法已对此做了更强约束:class 构造器中若 return 非原始值(即返回对象),会直接抛出 TypeError。这是语言层面对错误模式的主动拦截。
- 不要试图绕过——例如用箭头函数模拟构造、或用 eval 动态执行 return 语句
- 已有旧代码含此类逻辑,应统一重构为工厂模式,并显式标注返回类型(如 JSDoc 标注
@returns {User}) - TypeScript 用户可开启
noImplicitReturns和自定义 lint 规则,提前捕获构造函数中异常的 return 分支
验证继承完整性,而非仅依赖 constructor 属性
很多人误以为检查 obj.constructor === MyClass 就够了,其实这只是表象。真正关键的是原型链是否连通。
- 用
Object.getPrototypeOf(obj) === MyClass.prototype确认实例直系原型正确 - 用
obj instanceof MyClass测试运行时多态能力,比 constructor 更可靠 - 在关键对象创建后插入轻量校验,例如:
if (!(user instanceof User)) throw new Error('User construction failed')
组合场景下避免“伪继承”陷阱
当用组合代替继承时(如 A 类内部持有 B 实例),切勿在 A 的构造函数中 return this.b 或类似操作——这会让调用方误以为得到了 A,实际拿到的是 B,彻底混淆 is-a 关系。
- 组合就老老实实暴露委托接口,例如
a.getName()内部调用this.b.getName(),而非假装自己是 B - 若确实需要向上转型能力,应回归真实继承,或使用接口/抽象类明确定义契约
- 对混合了组合与继承的复杂类,建议用
Symbol.hasInstance自定义 instanceof 行为,但仅限极少数受控场景











