constructor属性是可读写的普通引用,非不可变元数据,重写原型易导致其被篡改;应避免依赖它做关键判断,优先用instanceof或object.getprototypeof等更可靠方式。

JavaScript 中 constructor 属性本身只是一个指向构造函数的引用,它默认存在于每个对象的原型上,用于标识“这个对象是被哪个函数创建出来的”。但它不是不可变的元数据,而是一个可读写的普通属性——这正是重定向风险的根源。
constructor 属性的默认行为与用途
当通过构造函数(如 new Array()、new Date() 或自定义类)创建实例时,实例的 __proto__ 会指向该构造函数的 prototype,而该 prototype 对象默认自带一个 constructor 属性,指向原构造函数。
常见用途包括:
- 运行时判断实例类型(虽不如
instanceof或Object.prototype.toString.call()可靠) - 在原型方法中动态创建同类新实例(例如
this.constructor()) - 序列化/反序列化时恢复构造意图(需配合额外逻辑)
constructor 被篡改的典型场景
由于 constructor 是普通属性,任何对原型的直接赋值都可能意外覆盖它:
-
MyClass.prototype = { method() {} };—— 完全覆盖原型对象,丢失默认constructor -
Object.assign(MyClass.prototype, { method() {} });—— 若未显式保留constructor,也会丢失 - 第三方库或手写继承代码中未修复原型链(如旧式寄生组合继承漏写
SubType.prototype.constructor = SubType)
一旦被覆盖,obj.constructor 就可能指向 Object、Function,甚至完全无关的函数,导致类型识别失效或 this.constructor 调用出错。
安全使用 constructor 的实践建议
避免依赖 obj.constructor 做关键逻辑判断。若必须使用,应主动防护:
- 手动修复:在重写
prototype后立即补回,例如MyClass.prototype.constructor = MyClass; - 优先用更健壮的方式替代:
obj instanceof MyClass、Object.getPrototypeOf(obj) === MyClass.prototype,或Object.prototype.toString.call(obj) === '[object MyClass]'(需配合 Symbol.toStringTag) - 在工厂或克隆方法中,不要无条件调用
this.constructor;可改为显式传入构造器,或用Reflect.construct控制调用上下文
ES6 类中的 constructor 表现
使用 class 声明时,constructor 是语法必需的(即使空着也会隐式生成),且类的 prototype.constructor 不可枚举、不可配置(writable: true 但 configurable: false),降低了被意外覆盖的风险。但依然可通过 Object.defineProperty 强行修改——所以本质风险未消失,只是门槛略高。
子类继承时,super() 会确保父类 constructor 正确初始化,但若手动操作子类原型,仍需自行维护 constructor 指向。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











