前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
首页程序员面试题库Map 避免原型链属性冲突
JavaScript数组与集合

用普通对象当字典时为什么会读到原型属性,Map 能避免吗?

普通对象读属性会沿原型链查找,所以空对象 {} 读 toString 也能拿到一个函数,用它判断「这个键存过没有」就会误判。Map 的 get 和 has 只查自己存进去的条目,不会碰到原型上的方法。

前端进阶之旅 · 一题精讲更新于 2026.09.29
JavaScript#数组与集合
先看核心答案读代码示例
理解线索

查字典要分清三件事

  1. 键存不存在map.has(key) 或 Object.hasOwn(obj, key)
  2. 值是什么0、false、undefined 都可能是合法值
  3. 从哪里读对象属性会查原型链,Map 条目只查自身

Map 本身也是对象,也可以用 map.foo = 1 挂属性,但这个属性不是条目,get 查不到,size 也不算它。

核心回答

先记住这个答案

普通对象读属性时,自己身上没有就会去原型链上找。所以一个空对象 dict 读 dict['toString'] 会拿到 Object.prototype 上的方法,拿它判断「用户有没有存过 toString 这个键」就错了。Map 的 get 和 has 只在自己存进去的条目里查,原型上的方法不会被当成键,所以没有这个问题。对象方案也能补救,用 Object.hasOwn 判断自有属性,或者用 Object.create(null) 创建一个没有原型的对象。另外,判断键是否存在和取值要分开,值可能本来就是 0 或 undefined。

  • obj[key] 会查原型链,能读到 toString
  • Map 的条目和它自身的属性是两套存储
  • 判断键是否存在用 has 或 Object.hasOwn
  • 值可能是 0 或 false,别拿真假判断存在

空对象为什么能读到 toString

做一个单词计数功能,用户输入的单词当键。用户恰好输入了 toString 或 constructor,普通对象就出问题了。

对象字典和 Map 对照JavaScript
const dict = {}
const map = new Map()

console.log(typeof dict.toString, Object.hasOwn(dict, 'toString'))  // 读到了继承的方法
console.log(map.has('toString'))                                    // Map 里没有这个条目

map.set('toString', undefined)                    // 存一个值为 undefined 的条目
console.log(map.has('toString'), map.get('toString') === undefined)

map.extra = 1                                     // 给 Map 对象挂一个普通属性
console.log(map.has('extra'), map.size)           // 不是条目,size 也不算
查看输出与解释
function false
false
true true
false 1

第一行说明 dict.toString 能读到一个函数,但 Object.hasOwn 返回 false,这个函数是从原型继承来的,不是字典里存的。Map 没有这个问题,没 set 过的键 has 就是 false。 最后一行说明 Map 有两套数据:set 存进去的条目,和它作为对象身上的普通属性。map.extra = 1 只是加了一个属性,has 查不到,size 也不算。用 Map 就只用 set、get 和 has 这些方法,不要混用方括号。

用对象当字典时的两种补救

如果键都是字符串,又不想换成 Map,普通对象也能安全地用,关键是不要走原型链。

Object.hasOwn 和无原型对象
判断存在用 Object.hasOwn(dict, key),它只看对象自己的属性。老写法 dict.hasOwnProperty(key) 有隐患:如果用户存了一个叫 hasOwnProperty 的键,这个方法就被覆盖了。 另一种办法是 Object.create(null) 创建字典,它没有原型,读 toString 就是 undefined。但它也就没有 hasOwnProperty 这些方法可以调用了,判断存在还是用 Object.hasOwn 或 in。
回答前,多想一步

容易答错的地方

用 if (cache[key]) 判断缓存命中
这个写法有两个问题。一是会命中原型上的属性;二是值为 0、false、空字符串时会被当成没命中,于是每次都重新计算。应该先判断键存不存在,存在再取值。
以为 map.get(key) 返回 undefined 就是没存过
Map 允许存 undefined 作为值,这时 get 的结果和没存过一样。缓存里需要区分「算过但结果为空」和「没算过」,要用 has。
以为换成 Map 就杜绝了原型污染
Map 只解决了它自己的查询问题。如果代码其他地方还在把用户输入深度合并到普通对象里,比如处理 __proto__ 这样的键,原型污染风险照样存在,要在合并逻辑里过滤这些危险键。
试着用自己的话回答

面试官还会怎么问?

Object.create(null) 和 Map 是一回事吗?

不是。Object.create(null) 仍然是普通对象,键只能是字符串或 Symbol,没有 size,统计数量要用 Object.keys。Map 的键可以是任意值,包括对象,还自带 size 和按插入顺序遍历。

in 和 Object.hasOwn 有什么区别?

key in obj 会查原型链,'toString' in {} 是 true。Object.hasOwn(obj, key) 只查对象自己,Object.hasOwn({}, 'toString') 是 false。字典场景要用后者。

map.get 读不存在的键会报错吗?

不会,返回 undefined。想在没有时给默认值,可以写 map.has(key) ? map.get(key) : defaultValue;如果确定不会存 undefined,用 map.get(key) ?? defaultValue 更简洁。

从一道题,走向一组知识

把知识连起来

数组与集合

Map.size 和 Object.keys(obj).length 数的到底是什么?

同样是分清 Map 条目和对象属性这两套数据,那篇讲统计数量时 size 和 Object.keys 各数的是什么。

数组与集合

JSON.stringify(Map) 为什么得到空对象,怎样可靠保存再恢复?

Map 条目不是对象属性,所以 JSON.stringify 看不到它们,那篇讲怎么正确序列化。

参考资料

  • MDN:Map
  • MDN:Object.hasOwn

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. 空对象为什么能读到 toString
  3. 用对象当字典时的两种补救
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑