JavaScript 原型链与包装对象:继承查找和安全扩展
本文目录
JavaScript 对象拥有内部原型链接。读取一个属性时,先查对象自身,再沿原型链逐级查找,直到属性命中或链尾 null。class 只是更易读的构造与原型语法,不会改变这套对象模型。
构造函数、实例和共享方法
new Constructor() 会创建实例并将其内部原型链接到 Constructor.prototype。实例可以读取原型上的方法,所有实例共享同一个函数对象;实例自己的同名属性会遮蔽原型属性。可用 Object.getPrototypeOf(instance) 检查原型,不要在新代码中依赖旧的 __proto__ 访问器。
class Student {
constructor(name) {
this.name = name;
}
introduce() {
return '我是 ' + this.name;
}
}
const first = new Student('小明');
const second = new Student('小红');
Object.getPrototypeOf(first) === Object.getPrototypeOf(second); // true
first.introduce(); // 我是 小明
普通对象的 constructor 属性可以被覆盖,因此不能单独作为可靠类型证明。instanceof 依赖原型链,在 iframe 等不同 realm 中可能对内建类型返回意外结果。跨模块数据更适合验证明确字段、使用 schema 或判别联合,而不是相信对象构造器。
原始值与临时包装
字符串、数字、布尔值等原始值不是可变对象;读取 'hello'.toUpperCase() 时,语言会提供相应方法调用能力,但表达式不会让原始变量永久变成 String 对象。一般使用原始字面量,避免手动 new String / new Number;包装对象与原始值比较、布尔判断和序列化都可能令人困惑。
不要给内建原型随意加方法
扩展 String.prototype 或 Array.prototype 会影响全局同类对象,可能与浏览器、polyfill、测试框架或其他依赖冲突,还会改变继承属性枚举行为。写工具函数或独立模块更容易审计。只有标准化兼容补丁且完整遵循规范、特性检测和部署策略时,才考虑 polyfill。
原型链还带来安全边界:把不可信键递归合并到普通对象可能修改共享原型,形成原型污染。外部数据用 schema 和字段 allowlist 处理;字典可用 Map 或 null prototype 对象。对象属性中的 __proto__、constructor 和 prototype 不应在未经验证时进入通用赋值/合并逻辑。
检查清单
- 哪些行为属于每个实例,哪些行为可以安全地在原型共享?
- 代码是否把
__proto__、constructor当作标准类型检测? - 是否修改了浏览器内建 prototype?
- 外部键值合并会不会遍历原型或触发危险 setter?
- 是否能用 schema、Map 或组合代替隐式继承?
参考:MDN Inheritance and the prototype chain、MDN Object.getPrototypeOf、MDN instanceof、MDN Primitive、OWASP Prototype Pollution Prevention。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。