JavaScript 对象与工厂函数:原型共享、属性检查和引用语义
本文目录
对象是动态属性集合,常见建模方式有对象字面量、工厂函数、构造函数和 class。选择取决于是否需要实例身份、共享行为、封装和继承;不是所有数据都需要 class。
工厂与构造函数
工厂函数显式返回对象,适合隐藏创建过程或组合数据;构造函数配合 new 使用,运行时会创建实例、连接其原型、以实例作为 this 调用函数,并按规则返回结果。class 是原型机制的语法抽象,并不会消除原型链。
把方法在构造函数内部每次创建会产生不同函数对象;相同且无实例私有状态的方法通常放到原型或 class prototype 上共享。公共原型方法中的 this 指向调用时的接收对象;如果方法被单独取出,调用行为可能改变。
function createPerson(name) {
return {
name,
greet() {
return '你好,' + this.name;
},
};
}
class Counter {
#value = 0;
increment() {
this.#value += 1;
return this.#value;
}
}
工厂适合轻量组合;需要私有实例状态和稳定接口时可以使用 class/private fields;大型应用优先组合职责,不要为继承而继承。
属性查找和安全检查
读取 obj.name 会先找对象自身属性,再沿原型链向上找。'name' in obj 同时包含继承属性;Object.hasOwn(obj, 'name') 只检查 own property。遍历普通对象时,for...in 也可能枚举到可枚举继承属性;只读自有键时用 Object.keys / Object.entries。
外部数据不应被当作任意对象属性字典。对对象执行不受限的合并可能引入原型污染风险;字段写入前使用 schema 和 allowlist,数据字典优先用 Map,确需纯字典时可考虑 Object.create(null) 并测试序列化兼容。
const profile = Object.create(null);
profile.displayName = '读者';
Object.hasOwn(profile, 'displayName'); // true
'toString' in profile; // false
值、引用与 JSON
把对象赋给另一个变量或作为参数传递,会复制引用值而不是复制整棵对象。通过任一引用修改同一属性,其他引用都能观察到;把参数重新绑定到新对象不会改变调用方变量。用结构化克隆或领域映射创建真正独立数据,参见深拷贝专题。
JavaScript 对象字面量常被口头称作“JSON 对象”,但 JSON 是一种文本交换格式,标准 JSON 不含函数、undefined、注释、循环引用或原型信息。用 JSON.parse 处理外部文本时捕获解析错误,并在读取字段前校验结构。不要因为对象长得像 JSON 就信任其字段。
检查清单
- 是否真的需要实例对象,还是简单工厂返回值即可?
- 重复方法是否适合放在原型或共享模块中?
- 属性检查需要包含继承属性吗?
- 用户数据是否参与不受限的对象合并或键访问?
- 引用共享是设计意图还是意外副作用?
参考:MDN Inheritance and the prototype chain、MDN Object.hasOwn、MDN Object.create、MDN Map、OWASP Prototype Pollution Prevention。
Live Notes
留言提交后会先进入审核,审核通过后对其他读者公开。