防范第三方混淆工具破坏原型继承,核心是守住构造函数名可识别性与原型链完整性;应避免硬依赖constructor.name,改用instanceof、Symbol.toStringTag、静态标识或构建时注入元数据等方式保障类型判断可靠性。

防范第三方混淆工具破坏原型继承中构造函数名称,核心是守住 构造函数名的可识别性 和 原型链的完整性。混淆工具(如 Terser、UglifyJS、Webpack 的 production 模式)常将 function Person() {} 压缩为 function a() {},导致 instance.constructor.name === "a",进而使依赖构造函数名的逻辑(如序列化、反序列化、类型判断、调试工具、框架元数据推导)全部失效。
避免在运行时硬依赖 constructor.name
这是最直接有效的防线。constructor.name 是非标准、易被篡改、且在严格模式或箭头函数中不可靠的属性。
- 用
Object.prototype.toString.call(obj)替代obj.constructor.name判断内置类型(如[object Array]、[object Date]) - 对自定义类,显式添加静态标识:
Person.type = 'Person'或Person[Symbol.toStringTag] = 'Person',后者能让Object.prototype.toString.call(new Person())返回[object Person] - 需要精确类型匹配时,优先用
instanceof或isPrototypeOf(),它们基于原型链而非字符串名,不受混淆影响
配置混淆器保留关键构造函数名
多数现代混淆工具支持白名单机制,可明确指定不压缩特定标识符。
- Terser:在
terserOptions中加入keep_fnames: true或keep_classnames: true;更精细控制可用reserved数组,例如["Person", "User", "APIResponse"] - Webpack 5+:在
optimization.minimizer配置中透传 Terser 选项,确保上述设置生效 - Rollup + @rollup/plugin-terser:同样通过
keepClassNames或keepFnames启用
用 Object.create(null) + 显式 prototype 绑定替代隐式构造函数推导
当构造函数名不可靠时,主动切断对它的依赖,转而用可控方式建立原型关系。
- 创建“干净字典”对象时,统一用
Object.create(null),避免任何constructor属性干扰 - 若需模拟类行为但规避 name 问题,可手动设置原型:
const inst = Object.assign(Object.create(Person.prototype), { name: 'Alice' });,再补上inst.constructor = Person(即使 Person 被重命名,只要引用还在即可) - 搭配
Object.defineProperty锁定 constructor 属性的可写性:Object.defineProperty(inst, 'constructor', { value: Person, writable: false, enumerable: false })
构建时注入类型元数据,绕过运行时推断
把类型信息从“动态推导”变成“静态注入”,彻底脱离 constructor.name 的脆弱性。
- 使用 TypeScript 编译后的
__metadata或自定义装饰器,在类定义处写入Person.$$type = 'Person' - 借助 Babel 插件(如
@babel/plugin-transform-typescript或自研插件),在 AST 层自动为带特定注解的类注入不可混淆的静态字段 - 服务端或构建产物中生成类型映射表(JSON),前端加载后通过实例的唯一特征(如自有属性结构)匹配类型,不查 name


















