如何对JavaScript原生原型链进行保护与防御

来源:Java编程网作者:小何头衔:草根站长
导读:本期聚焦于小伙伴创作的《如何对JavaScript原生原型链进行保护与防御》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《如何对JavaScript原生原型链进行保护与防御》有用,将其分享出去将是对创作者最好的鼓励。

JavaScript原型链基础回顾

JavaScript中的每个对象都有一个隐式原型属性__proto__,指向其构造函数的原型对象,而原型对象本身也是对象,也有自己的隐式原型,这样层层向上直到Object.prototype,最终指向null,形成的链式结构就是原型链。当我们访问对象的某个属性时,会先在对象自身查找,找不到则沿着原型链向上查找,直到找到属性或者到达原型链末端。

如何对JavaScript原生原型链进行保护与防御

比如我们创建一个简单的构造函数:

// 定义构造函数
function Person(name) {
    this.name = name;
}
// 给原型添加方法
Person.prototype.sayName = function() {
    console.log(this.name);
};
// 创建实例
const person1 = new Person('张三');
// 实例本身没有sayName方法,会沿着原型链找到Person.prototype上的方法
person1.sayName(); // 输出:张三

原型链面临的风险场景

原型污染攻击

原型污染是最常见的原型链风险,攻击者可以通过修改Object.prototype等原生原型,给所有对象添加恶意属性或者覆盖原有方法。比如在不安全的对象合并场景中,如果直接把用户输入的对象的属性合并到目标对象,且目标对象是普通对象,就可能触发原型污染:

// 不安全的对象合并函数
function unsafeMerge(target, source) {
    for (const key in source) {
        // 没有对key做校验,可能修改到__proto__属性
        target[key] = source[key];
    }
    return target;
}
// 模拟攻击者输入
const maliciousInput = JSON.parse('{"__proto__": {"isAdmin": true}}');
const userObj = {};
unsafeMerge(userObj, maliciousInput);
// 所有普通对象都被污染了
console.log({}.isAdmin); // 输出:true

原生原型方法被覆盖

如果代码不小心覆盖了原生原型上的内置方法,比如Array.prototype.pushObject.prototype.toString,会导致所有依赖这些方法的代码出现异常,这种问题在大型项目中排查起来非常困难。

原型链保护与防御方法

冻结原生原型对象

可以使用Object.freeze方法冻结原生原型对象,防止其被修改。冻结后的对象不能添加新属性,不能删除已有属性,也不能修改已有属性的可写性、可配置性、可枚举性,也不能修改已有属性的值。

// 冻结Object的原型
Object.freeze(Object.prototype);
// 尝试给Object.prototype添加属性,在严格模式下会报错,非严格模式下静默失败
Object.prototype.newProp = 'test';
console.log(Object.prototype.newProp); // 输出:undefined

// 也可以冻结Array、Function等原生构造函数的原型
Object.freeze(Array.prototype);
Object.freeze(Function.prototype);

需要注意的是,冻结操作要在项目初始化的最早阶段执行,确保所有代码运行前原生原型已经被保护。如果已经存在对原型的修改,冻结后再执行修改操作会失效。

安全处理对象合并操作

在进行对象合并、属性赋值等操作时,要避免修改到__proto__属性,同时可以使用Object.create(null)创建无原型的对象作为目标对象,这类对象没有继承Object.prototype,不会被原型污染影响。

// 安全的对象合并函数
function safeMerge(target, source) {
    for (const key in source) {
        // 跳过__proto__和constructor等可能导致原型修改的属性
        if (key === '__proto__' || key === 'constructor') {
            continue;
        }
        // 只合并自身可枚举属性
        if (source.hasOwnProperty(key)) {
            target[key] = source[key];
        }
    }
    return target;
}
// 创建无原型对象作为目标
const safeTarget = Object.create(null);
const userInput = JSON.parse('{"__proto__": {"isAdmin": true}, "name": "李四"}');
safeMerge(safeTarget, userInput);
console.log(safeTarget.name); // 输出:李四
console.log(safeTarget.isAdmin); // 输出:undefined
// 普通对象也不会被污染
console.log({}.isAdmin); // 输出:undefined

避免直接修改原生原型

开发过程中尽量不要给原生原型添加自定义方法,如果确实需要给某个类型的实例添加公共方法,建议通过继承的方式实现,或者给实例单独添加方法,而不是直接修改构造函数的原型。如果必须修改,要做好兼容性校验,避免覆盖原有方法。

// 不推荐的做法:直接给Array原型添加方法
// Array.prototype.myMethod = function() {};

// 推荐的做法:创建自定义数组类继承Array
class MyArray extends Array {
    myMethod() {
        console.log('自定义方法');
    }
}
const arr = new MyArray(1, 2, 3);
arr.myMethod(); // 输出:自定义方法

使用hasOwnProperty校验属性归属

在遍历对象属性时,尽量使用hasOwnProperty方法判断属性是否为对象自身的属性,避免访问到原型链上的属性,尤其是在处理不确定来源的对象时,这个习惯可以有效规避原型污染带来的问题。

const obj = {};
// 假设原型被污染,添加了polluted属性
Object.prototype.polluted = 'bad';

// 错误做法:直接遍历for...in会拿到原型上的属性
for (const key in obj) {
    console.log(key); // 输出:polluted
}

// 正确做法:用hasOwnProperty过滤
for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
        console.log(key); // 没有输出,因为obj自身没有属性
    }
}

开发最佳实践建议

  • 项目启动阶段优先执行原生原型冻结操作,从根源上避免原型被意外修改。
  • 所有处理外部输入的对象操作,都要做属性名校验,禁止修改__proto__constructor等敏感属性。
  • 代码审查时重点关注是否有修改原生原型的逻辑,非必要不通过原型扩展功能。
  • 使用第三方库时,确认库没有修改原生原型的行为,避免引入原型污染风险。
  • 在严格模式下开发,修改冻结对象或者给只读属性赋值时会直接抛出错误,便于早期发现问题。

常见问题解答

冻结原型后还能正常使用内置方法吗

可以,冻结原型只是禁止修改原型对象本身,不会影响到原型上已有方法的正常调用,内置方法的读写性、可配置性在冻结前已经是设定好的,冻结操作不会改变这些特性。

无原型对象有什么使用限制

无原型对象没有继承Object.prototype的方法,所以不能直接调用toStringhasOwnProperty等方法,如果需要使用这些方法,可以通过Object.prototype.toString.call(obj)的方式调用。

const nullProtoObj = Object.create(null);
// 直接调用会报错
// nullProtoObj.toString();
// 正确调用方式
console.log(Object.prototype.toString.call(nullProtoObj)); // 输出:[object Object]
console.log(Object.prototype.hasOwnProperty.call(nullProtoObj, 'name')); // 输出:false

JavaScript原型链原型链保护原型链防御修改时间:2026-06-19 20:12:37

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。