用 Object.create(null) 创建对象字典最安全,因其无原型链,避免 toString 等键名冲突;但需过滤 __proto__、constructor 等敏感键,并推荐白名单或 Symbol 键名替代。

创建对象字典时,用 Object.create(null) 是最直接有效的办法——它不继承 Object.prototype,自然就不会有 toString、hasOwnProperty、constructor 这些方法名冲突的问题。
为什么普通空对象 {} 会出问题
{} 默认继承自 Object.prototype,所以自带一堆方法。一旦你把数据字段设为 "toString" 或 "hasOwnProperty",就会覆盖原型上的同名方法,导致后续调用异常或逻辑错乱。比如:
-
const dict = { toString: 'user-123' }→dict.toString()返回字符串,不再是默认的[object Object] -
for...in遍历时可能误读原型方法(尤其在未加hasOwnProperty判断时) - 第三方脚本若污染
Object.prototype,你的字典也可能被意外影响
用 Object.create(null) 创建真正干净的字典
它返回一个“无原型”的对象,既没有 __proto__,也没有任何继承属性,查不到键就直接返回 undefined,不会向上遍历:
-
const map = Object.create(null)—— 安全起点 - 支持常规操作:
map.id = 100、map['name'] = 'Alice'、delete map.id - 但注意:它没有
toString、hasOwnProperty等方法,需手动补充或改用Object.prototype.hasOwnProperty.call(map, key)
敏感键名必须过滤,不能只靠“不继承”
即使用了 Object.create(null),如果外部输入控制了键名(如 JSON 解析、URL 参数),仍要拦截危险键,防止原型污染入口被绕过:
- 明确禁止:
"__proto__"、"constructor"、"prototype"及其大小写变体(如"__PROTO__") - 深合并场景下,每一层递归前都要检查当前
key,不能只校验顶层 - 更推荐白名单机制:只允许已知安全字段(如
["id", "name", "email"])进入目标对象
替代方案:Symbol 作键名也能规避冲突
如果你需要保留原型链但又想彻底避免字符串键名碰撞,可以用 Symbol 作为属性名:
-
const idKey = Symbol('id'),然后dict[idKey] = 100 - 每个
Symbol()都是唯一值,不同模块定义的同名Symbol('id')也不会覆盖 -
Symbol键不会出现在for...in、Object.keys()或JSON.stringify()中,需用Object.getOwnPropertySymbols()显式获取
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











