防范原型污染需工程化约束:启用eslint的no-extend-native等规则并设为error级,入口处冻结原生原型,用object.create(null)替代{},并建立团队修改红线文化。
防范内部开发人员滥用原型扩展原生对象,核心不是靠信任或口头约定,而是把限制嵌进工程流程里——让错误在写出来之前就被拦住,在提交之前就报错,在运行之前就失效。
从 Lint 规则强制拦截修改行为
真正起作用的约束,是编辑器里标红、CI 上构建失败。项目必须启用明确禁止原型污染的静态检查规则:
- ESLint 配置中启用 no-extend-native 规则,并设为
"error"级别,它会直接报错Array.prototype.map = ...这类赋值 - JSHint 中启用 freeze: true,它对
Object.prototype、String.prototype等所有原生原型的任何属性定义都视为违规 - 配合 no-implicit-globals 和 no-eval,堵住通过动态执行绕过检查的路径
在入口处冻结原型,切断运行时篡改可能
静态检查能防住大部分,但无法覆盖已加载的第三方脚本或动态 eval。必须在 JS 执行最早期做兜底:
- 在
<script></script>标签中第一个执行的代码块里,依次调用Object.freeze(String.prototype)、Object.freeze(Array.prototype)、Object.freeze(Object.prototype)等 - 注意顺序:该脚本必须 早于所有业务代码和第三方 SDK 加载,推荐放在
内最顶部的内联 script 中 - 冻结后若某处仍尝试修改,非严格模式下静默失败,严格模式下直接抛
TypeError,可被全局错误监控捕获
用无原型对象替代普通对象字面量
很多原型污染其实源于“只是想存个配置”,却误用了 {}。这类场景应主动规避继承链:
- 所有仅作键值容器的场景(如路由参数缓存、插件注册表、临时合并对象),统一改用
Object.create(null) - 它没有
__proto__、不继承toString或hasOwnProperty,天然免疫__proto__注入 - 需要判断属性存在时,改用
Object.prototype.hasOwnProperty.call(obj, key)替代obj.hasOwnProperty(key)
建立团队协作中的原型修改红线文化
技术手段之外,要让所有人知道“为什么不能动”:
- 在代码规范文档中单列一条:“禁止向原生构造器原型添加/覆盖方法,包括但不限于 String、Array、Object、Boolean、Function”
- 新成员入职时演示一个真实案例:比如
Array.prototype.flatten被重写后,导致某个依赖库的深比较逻辑失效,引发线上数据错乱 - 在 PR 模板中加入检查项:“✅ 未修改任何原生原型”;Code Review 时对此项必须确认











