先记住这个答案
普通对象的 obj[key] 会沿原型链查找,因此空对象读取 toString 等名称也可能得到继承方法,用它判断业务键存在会误判。Map 的 get、has 查询的是集合内部条目,不会把 Map 原型方法当成业务键;对象方案可使用 Object.hasOwn,或根据需求创建 null 原型字典。还要把存在性与取值分开:已存 undefined 的键与不存在的键都可能 get 到 undefined,零和 false 也不能用真假判断。Map 解决这一层键空间问题,但不替代输入校验或其他位置的属性写入防护。
- 普通方括号读取包含原型链
- Map 条目与 Map 对象自身属性是两套存储
- 存在性用 has 或 hasOwn,不用值的真假
空字典也可能读出继承的方法
下面普通空对象读取 toString 得到函数,但 Object.hasOwn 证明它并没有这个自有业务字段。空 Map 对同名键 has 返回 false,直到显式 set 才会出现条目。
示例给 Map 存一个值为 undefined 的键,get 结果与未命中可能相同,但 has 仍能区分。缓存接口若允许空结果,应保留这种存在信息,避免每次把已缓存空值误认为需要重新计算。
const object = {};
const map = new Map();
console.log(typeof object.toString, Object.hasOwn(object, 'toString'));
console.log(map.has('toString'));
map.set('toString', undefined);
console.log(map.has('toString'), map.get('toString') === undefined);
map.extra = 1;
console.log(map.has('extra'), map.size);查看输出与解释
function false
false
true true
false 1Map 的普通 extra 属性不会变成集合条目,也不会增加 size。接口应统一使用 set/get/has,混用方括号和集合方法会产生两份看似同名但互不相通的数据。
对象字典也可以有明确的安全读取方式
若数据只需要固定字符串字段,Object.hasOwn 能检查自有属性,并且不依赖对象是否覆盖了 hasOwnProperty 方法。null 原型对象还可移除继承属性,但它不会自动变成支持任意对象键的 Map。
使用外部键做配置合并时,读取、赋值和深层路径写入是不同操作。某个地方改成 Map 并不能证明其他对象合并逻辑都安全,应按真实数据流检查允许字段和更新方式。
不要把假值当作缓存未命中
计数零、权限 false 或空字符串可能都是合法业务结果。如果用 if (cache[key]) 判断存在,就会错误地走未命中分支;应先检查键是否存在,再决定如何处理对应值。
Map 的 get 也有相同取值歧义,应该配合 has 或使用明确的结果包装。需要跨网络传递缓存内容时,还要考虑 Map 的序列化规则,不能把内部条目当成普通可枚举属性直接输出。
容易答错的地方
- 用 obj[key] 的真假判断是否记录过
- 这既可能命中继承属性,也会漏掉零、false 等自有假值。应使用明确存在性检查,并把合法值域写进接口契约,避免缓存和配置读取产生隐蔽分支错误。
- 认为使用 Map 后所有原型相关风险都消失
- Map 的条目查询避开对象属性链,但程序可能仍把数据写回普通对象或执行深层合并。需要沿完整读写路径校验字段和操作,不能把一种容器选择扩大为整个系统的安全保证。
面试官还会怎么问?
Object.create(null) 和 Map 一样吗?
不一样,前者仍按对象属性规则管理字符串或 Symbol 键,没有 Map 的任意键身份和集合 API。它适合某些纯字典需求,但枚举、计数和序列化仍需按对象规则处理。
可以直接调用 obj.hasOwnProperty 吗?
对象可能覆盖同名方法,null 原型对象也可能没有该方法。使用 Object.hasOwn 或安全借用原型方法能避免依赖实例上的同名属性,但仍应核对目标环境支持。
Map.get 读取不存在键会抛错吗?
通常返回 undefined,不会仅因未命中而抛错。若业务把 undefined 作为合法缓存值,应使用 has 区分存在与缺失,再决定默认值或重新计算,不能只检查 get 的结果。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。