object.create(null)创建的对象[[prototype]]为null,不继承object.prototype任何方法,彻底避免属性冲突、原型污染和隐式调用异常,是构建纯净数据容器最可靠方式。

直接用 Object.create(null) 就行。它创建的对象内部 [[Prototype]] 为 null,不继承 Object.prototype 上的任何方法或属性,天然隔绝原型链污染,也省去原型查找开销,是构建高性能纯净容器最可靠的方式。
为什么必须用 Object.create(null) 而不是 {}
空对象 {} 实际等价于 Object.create(Object.prototype),自带 toString、hasOwnProperty、constructor 等可被覆盖的原型方法:
- 用户输入 key 为
"toString"时,会静默覆盖原型方法,后续调用obj.toString()报错或返回错误值 - 恶意输入可通过
__proto__或constructor注入属性(尤其在未冻结对象时) -
for...in遍历虽在现代引擎中通常不暴露原型属性,但语义上仍依赖原型链,逻辑不纯粹
初始化后要小心哪些操作会失败
因为没原型,以下常见写法会直接报 TypeError:
-
obj.toString()→obj.toString is not a function -
obj.hasOwnProperty('key')→ 同样报错 -
obj instanceof Object→ 返回false
替代方案更安全:
- 查键存在:用
'key' in obj(语言级操作,不依赖原型) - 枚举键名:用
Object.keys(obj)或Object.entries(obj) - 判断值是否定义:直接
obj.key !== undefined或typeof obj.key !== 'undefined' - 需要序列化:
JSON.stringify(obj)可直接用(现代引擎已支持),无需额外转换
典型适用场景与使用示例
适合所有需要“纯数据容器”且 key 不可控的场合:
-
路由映射表:
const routes = Object.create(null); routes['/api/user'] = handler; -
事件监听器存储:
const listeners = Object.create(null); listeners['click'] = []; -
JSON Schema 校验缓存,以 schema 路径为 key:
cache['#/definitions/user'] = validator; -
模块导出扁平化映射,避免
exports.default与原型default冲突
和 Map 的关键区别:别混淆用途
Object.create(null) 和 Map 解决不同问题:
-
Map支持任意类型 key、保持插入顺序、有size和迭代器接口,但不能点号取值、不能直接JSON.stringify、不是 plain object -
Object.create(null)是 plain object,兼容所有期望普通对象的旧代码和 JSON 流程,语法更轻量(obj.key或obj[key]),初始化略快 - 选哪个?需要点号语法或 JSON 兼容 → 用
Object.create(null);需要弱引用 key、遍历顺序敏感、或强类型保障 → 用Map










