真正阻止第三方库篡改原生全局变量,关键在于用 configurable: false 从属性定义层面锁死,配合 writable: false 并提前冻结关键原型对象(如 Object.freeze(Date.prototype)),且需在第三方脚本执行前完成。

要真正阻止第三方库篡改原生全局变量,不能只靠“声明时写死”,关键得用 configurable: false 从属性定义层面锁死它的可修改性——这会让 delete 失效、Object.defineProperty 无法重配置,连数据属性和访问器属性之间都不可互转。
锁定单个全局变量的 configurable
对明确需要保护的全局变量(如 window.JSON、Array.prototype.push),直接在原型或全局对象上用 Object.defineProperty 显式设 configurable: false:
-
必须显式传参:对象字面量或简单赋值(
window.myConst = 42)默认configurable: true,可被删可重定义;只有Object.defineProperty未指定configurable时才默认false -
组合 writable: false 更稳妥:仅设
configurable: false不防改值,搭配writable: false才能同时禁止赋新值和删除/重定义 -
注意作用对象:改
window上的变量,就操作window;改内置构造函数原型方法(如Date.prototype.toISOString),就得操作对应prototype
冻结整个原型链以防批量篡改
单个属性防护不够,第三方常批量覆盖原型方法。应对策略是提前冻结关键原型对象:
-
Object.freeze(Date.prototype)、Object.freeze(Array.prototype)等,能阻止新增、删除、重定义属性,且自动把所有自有属性的configurable设为false -
时机很重要:必须在第三方脚本执行前完成冻结,比如放在
<script>标签最顶部,或通过模块入口立即执行 -
浅冻结局限:
freeze只冻结第一层属性,若某属性值本身是对象(如JSON.parse返回的对象),需单独再freeze或用Object.seal
防范实例遮蔽与间接污染
即使原型属性不可配置,实例仍可通过赋值创建同名属性来“遮蔽”它,这不是篡改原型,而是绕过。需额外应对:
-
避免暴露可变引用:全局变量若指向对象,不要直接导出原始对象,而应返回防御性副本或不可变视图(如
Collections.unmodifiableMap的 JS 等价实现) -
监控非直接篡改:有些库通过 DOM 属性(如
data-config)反射修改配置,可用MutationObserver监听并同步还原内存值 -
不依赖 delete 检测:
configurable: false下delete window.X在非严格模式静默失败、返回false,不能当判断依据;应统一用Object.getOwnPropertyDescriptor检查描述符
与 preventExtensions / seal 的配合使用
不同防护层级各司其职,按需组合:
-
Object.preventExtensions(obj):最轻量,只禁新增属性,不影响已有属性的读写或删除 -
Object.seal(obj):比 preventExtensions 多一步——把所有自有属性configurable设为false,禁删禁增,但允许改值(writable不变) -
Object.freeze(obj):最强,等价于seal + 所有 writable 设为 false;适合最终态配置对象 -
优先 freeze 原型对象:比如
Object.freeze(Object.prototype)能一揽子封住所有继承自它的对象的扩展能力,但要注意可能影响某些依赖原型扩展的合法库

















