最有效防御是尽早冻结关键原型对象。必须在页面首个顶部执行object.freeze(object.prototype)等八类原型冻结,并配合object.create(null)、白名单校验、严格模式等业务层防护措施。

JavaScript 中原型对象属性被意外覆盖,核心在于防止外部代码篡改原生或自定义原型上的方法与属性。这不是“能不能改”的问题,而是“要不要允许改”——多数生产环境必须拒绝这种修改。
冻结关键原型对象
对原生构造函数的 prototype 使用 Object.freeze() 是最直接有效的防护手段。它让原型对象变为不可扩展、不可配置、不可写,后续任何赋值或删除操作都会静默失败(非严格模式)或抛出错误(严格模式)。
- 应在所有第三方脚本加载前尽早执行,例如放在入口文件顶部
- 需冻结常用原生类型:String、Number、Boolean、Array、Object、Function、Date、RegExp 等的 prototype
- 示例:
Object.freeze(String.prototype);
Object.freeze(Array.prototype);
Object.freeze(Object.prototype);
修复自定义构造函数的 prototype 覆盖问题
当手动重写构造函数的 prototype(如 MyClass.prototype = { ... })时,会丢失默认的 constructor 属性,还可能覆盖已有自有属性。这不是“被外部覆盖”,而是自身操作引发的意外丢失。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 必须显式恢复 constructor,且推荐用 Object.defineProperty 设置
enumerable: false,保持与原生行为一致 - 若原型上原有其他自有属性(如
MyClass.prototype.version),应先用 Object.getOwnPropertyDescriptors 提取,再用 Object.defineProperties 合并到新原型中 - 更稳妥的做法是避免整体赋值,改用 Object.setPrototypeOf(Sub.prototype, Super.prototype) 或直接使用 class extends
覆盖父类原型上的 getter/setter
如果父类通过 Object.defineProperty 在原型上定义了访问器属性(如 get abc()),子类不能靠简单赋值(Sub.prototype.abc = 'x')覆盖——这只会触发父类 setter,或创建一个同名但无意义的数据属性。
- 正确方式一:用 Object.defineProperty 在子类原型上重新定义该属性,明确指定
value或新的get/set - 正确方式二:在子类中用 class 语法声明同名访问器,语义清晰且自动绑定
- 本质是“屏蔽(shadowing)”而非“替换”,即在子类原型上建立更高优先级的自有属性
防御原型链污染(尤其 Node.js 场景)
当应用接受用户输入并递归合并对象(如 merge({}, userInput))时,恶意输入如 {"__proto__": {"admin": true}} 可能污染全局原型,导致所有对象获得意外属性。
- 避免使用不安全的深拷贝/合并库;改用白名单校验或结构化克隆(structuredClone)
- 服务端可主动检测并拦截含
__proto__、constructor、prototype的键名 - 结合 Object.freeze 对基础原型加固,形成双重防线
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










