前端进阶之旅前端进阶之旅
基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
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 和普通对象的键有什么本质区别,什么时候该用 Map?

普通对象的键只能是字符串或 Symbol,写 obj[1] 实际存的是 "1",拿对象当键会变成 "[object Object]"。Map 的键可以是任意值,数字 1 和字符串 "1" 是两个键,两个不同的对象也是两个键。

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

键存进去之后还是不是它自己

  1. 普通对象非 Symbol 的键一律转成字符串,1 和 "1" 是同一个键
  2. Map键原样保存,1 和 "1" 是两个键
  3. 对象键Map 按引用比较,字段相同的两个对象也是不同的键

Map 比较键用的是 SameValueZero 规则,和 === 基本一样,区别是 NaN 可以当键并且能被查到。

核心回答

先记住这个答案

普通对象的键只能是字符串或 Symbol,其他类型的值当键时都会先被转成字符串。所以 obj[1] 和 obj['1'] 是同一个属性,拿对象当键会变成 "[object Object]",不同的对象会互相覆盖。Map 的键可以是任意类型,包括对象、函数、NaN,存进去是什么就是什么,对象按引用区分。所以当键不是字符串,比如要给 DOM 节点或对象关联数据时,就需要 Map。另外 Map 自带 size、按插入顺序遍历、不会和原型上的属性冲突,频繁增删的场景也更合适。

  • 对象的键会被转成字符串,Symbol 除外
  • 拿对象当键,全都变成 "[object Object]"
  • Map 的键保留原类型,对象按引用区分
  • 键不确定、频繁增删、要计数时用 Map

同样的键,存进两种容器

分别用一个对象、另一个结构类似的对象、数字 1 和字符串 '1' 当键,看看两种容器里各存了几个。

对象键被转成字符串JavaScript
const user = { id: 1 }

const obj = {}
obj[user] = 'a'            // 键被转成 '[object Object]'
obj[{ id: 2 }] = 'b'       // 也是 '[object Object]',覆盖了上一个
obj[1] = 'one'             // 键被转成 '1'
console.log(JSON.stringify(Object.keys(obj)))
console.log(obj['1'] === obj[1])

const map = new Map()
map.set(user, 'a').set({ id: 2 }, 'b').set(1, 'one').set('1', 'string one')
console.log(map.size)      // 四个键互不影响
console.log(map.get(user), map.get(1), map.get('1'))
查看输出与解释
["1","[object Object]"]
true
4
a one string one

普通对象里只剩两个键。两个不同的对象都被 String() 转成了 "[object Object]",第二次赋值把第一次覆盖了;数字 1 被转成了 "1",所以 obj['1'] 和 obj[1] 读到的是同一个值。Map 里四个键都在,user 这个对象本身就是键,拿同一个引用就能取到值。

什么时候用 Map,什么时候用对象

最典型的 Map 场景是给对象关联额外数据。比如记录每个 DOM 节点被点击了几次,或者缓存某个对象的计算结果,键就是对象本身。用普通对象根本做不到,只能给每个对象编一个字符串 id。

其他适合 Map 的情况
键来自用户输入时,普通对象会和原型上的 toString、constructor 这些属性冲突,Map 没有这个问题。需要频繁增删键、随时知道有多少个键时,Map 有 size,引擎对频繁增删也做了优化。需要按插入顺序遍历时,Map 严格保证顺序,对象会把整数键挪到前面。 反过来,结构固定的数据,比如一个用户的 name、age 字段,用对象更自然。需要直接 JSON.stringify 发给后端时,对象也更方便,Map 要先转成数组。
回答前,多想一步

容易答错的地方

以为对象的键可以是数字
obj[1] = 'x' 看起来键是数字,其实存进去的是字符串 "1",调用 Object.keys(obj) 返回的是 ["1"]。数组的索引也一样,本质上都是字符串键。
以为 Map 会按对象内容比较键
map.set({ id: 1 }, 'a') 之后,map.get({ id: 1 }) 是 undefined,因为这是两个不同的对象。要按内容查找,就用内容里的某个稳定字段当键,比如 map.set(user.id, user)。
试着用自己的话回答

面试官还会怎么问?

Map 和 WeakMap 的键有什么区别?

Map 的键可以是任意值,而且会一直引用着键,对象键不会被垃圾回收。WeakMap 的键只能是对象或非注册的 Symbol,对键是弱引用,键对象没有别的地方引用时,这个条目会被自动回收,但它不能遍历,也没有 size。

NaN 能当 Map 的键吗?

能。Map 用 SameValueZero 规则比较键,NaN 被认为等于 NaN,所以 map.set(NaN, 1) 之后 map.get(NaN) 能取到 1。+0 和 -0 也被当作同一个键。

对象和 Map 怎么互相转换?

对象转 Map 用 new Map(Object.entries(obj)),反过来 Map 转对象用 Object.fromEntries(map)。反向转换时非字符串的键会被转成字符串,对象键会丢失,所以只适合键都是字符串的 Map。

从一道题,走向一组知识

把知识连起来

数组与集合

flat 默认展开几层?flatMap 和先 map 再 flat 有什么不同?

同样是数组与集合 API 里容易被默认行为坑到的地方,flat 默认只展开一层。

数组与集合

什么是类数组对象?arguments 和真数组有什么区别,怎么转换?

类数组对象的数字索引其实也是字符串键,和这里讲的对象键会被转成字符串是一回事。

放回知识体系中复习JavaScript 进阶知识

参考资料

  • Map

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

本题目录
  1. 先记住这个答案
  2. 同样的键,存进两种容器
  3. 什么时候用 Map,什么时候用对象
  4. 容易答错的地方
  5. 面试官还会怎么问
  6. 把知识连起来
读懂,再试着讲出来

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

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