Map vs Object
Map vs Object
一句话:Map的键可以是任何类型、保证插入顺序、有 size、能直接遍历;对象的键只能是字符串或 symbol,而且带着一条原型链。
Object | Map | |
|---|---|---|
| 键的类型 | 字符串 / symbol(数字会被转成字符串) | 任何值,包括对象和函数 |
| 顺序 | 整数键会被排序,其余按插入 | 严格按插入顺序 |
| 大小 | Object.keys(o).length | map.size |
| 遍历 | 要先 Object.entries | 本身可迭代,直接 for…of |
| 继承来的键 / 原型污染 | 有风险 —— o["toString"]本来就有值;用户输入的 __proto__ 能改原型 | 没有,Map 是干净的 |
| JSON 序列化 | 直接可以 | 不行,要先转数组 |
怎么选:
- 用 Object:结构固定的记录(一个用户、一份配置),要 JSON 序列化,字段名写死在代码里。
- 用 Map:键是动态的、会频繁增删、数量大、键不是字符串。
会追问:「拿对象当字典有什么风险?」—— 有两个。①继承来的键:空对象也「有」toString、constructor,if (dict[key]) 遇到用户输入"constructor" 会误判成存在(用 Object.hasOwn 判断);②原型污染:键来自用户输入时,dict["__proto__"] = … 改的是对象的原型,递归合并时还能改到 Object.prototype,影响全局。Map 两个问题都没有,非要用对象就 Object.create(null)。
「WeakMap 呢?」—— 键必须是对象,而且不阻止垃圾回收。适合给对象挂额外数据又不想造成内存泄漏。
In one line: a Map takes keys of any type, guarantees insertion order, has size, and is iterable on its own; an object’s keys can only be strings or symbols, and it drags a prototype chain along with it.
Object | Map | |
|---|---|---|
| Key types | String / symbol (numbers become strings) | Any value, objects and functions included |
| Order | Integer keys get sorted, the rest are insertion order | Strictly insertion order |
| Size | Object.keys(o).length | map.size |
| Iteration | Needs Object.entries first | Iterable itself — for…of just works |
| Inherited keys / prototype pollution | A risk — o["toString"] already has a value, and a user-supplied __proto__ can change the prototype | None; a Map is clean |
| JSON serialisation | Works directly | No — convert to an array first |
How to choose:
- Use an Object for records with a fixed shape (one user, one config), for anything you serialise to JSON, and when the field names are written into the code.
- Use a Map when the keys are dynamic, entries come and go often, there are a lot of them, or the keys are not strings.
Follow-up: “What is risky about using an object as a dictionary?” — two things. ① Inherited keys: even an empty object “has” toString and constructor, so if (dict[key]) reports a hit when the user types "constructor" (check with Object.hasOwn). ② Prototype pollution: with user-supplied keys, dict["__proto__"] = … changes the prototype, and a recursive merge can reach Object.prototype and affect everything. A Map has neither problem; if you must use an object, build it with Object.create(null).
“And WeakMap?” — its keys must be objects, and it does not hold off garbage collection. Good for hanging extra data on an object without leaking memory.