DrillLab
第 23 / 105 道23 / 105 · #287

Map vs Object

Map vs Object

先自己答,再往下看Answer it yourself first

一句话:Map键可以是任何类型保证插入顺序、有 size、 能直接遍历;对象的键只能是字符串或 symbol, 而且带着一条原型链。

ObjectMap
键的类型字符串 / symbol(数字会被转成字符串任何值,包括对象和函数
顺序整数键会被排序,其余按插入严格按插入顺序
大小Object.keys(o).lengthmap.size
遍历要先 Object.entries本身可迭代,直接 for…of
原型污染有风险 —— o["toString"]本来就有值没有,Map 是干净的
JSON 序列化直接可以不行,要先转数组

怎么选:

  • 用 Object:结构固定的记录 (一个用户、一份配置),要 JSON 序列化, 字段名写死在代码里。
  • 用 Map:键是动态的、会频繁增删、 数量大、键不是字符串。

会追问:「为什么说对象有原型污染风险?」—— 因为空对象也「有」toStringconstructor 这些继承来的键。 拿对象当字典时,if (dict[key]) 遇到用户输入"constructor" 会误判成存在。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.

ObjectMap
Key typesString / symbol (numbers become strings)Any value, objects and functions included
OrderInteger keys get sorted, the rest are insertion orderStrictly insertion order
SizeObject.keys(o).lengthmap.size
IterationNeeds Object.entries firstIterable itself — for…of just works
Prototype pollutionA risk — o["toString"] already has a valueNone; a Map is clean
JSON serialisationWorks directlyNo — 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: “Why is an object a prototype pollution risk?” — because even an empty object “has” inherited keys like toString and constructor. Use an object as a dictionary and if (dict[key]) reports a hit when the user types "constructor". A Map does not have this 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.

JavaScript两个真实会踩的差别Two differences you will actually hit示意Illustrative
1// 对象的键会被转成字符串
2const o = {};
3o[1] = "a";
4o["1"] = "b";
5console.log(o); // { "1": "b" } ← 只有一个键!
6
7const m = new Map();
8m.set(1, "a").set("1", "b");
9console.log(m.size); // 2 ← 数字 1 和字符串 "1" 是不同的键
10
11// 原型污染
12const dict = {};
13console.log(dict["toString"]); // ƒ toString() ← 凭空有值
14console.log(new Map().get("toString")); // undefined ✓ 干净
15
16// Map 不能直接 JSON
17JSON.stringify(m); // "{}" ← 全丢了
18JSON.stringify([...m]); // '[[1,"a"],["1","b"]]' ✓
1// Object keys are converted to strings
2const o = {};
3o[1] = "a";
4o["1"] = "b";
5console.log(o); // { "1": "b" } ← only one key!
6
7const m = new Map();
8m.set(1, "a").set("1", "b");
9console.log(m.size); // 2 ← the number 1 and the string "1" are different keys
10
11// Prototype pollution
12const dict = {};
13console.log(dict["toString"]); // ƒ toString() ← a value appears from nowhere
14console.log(new Map().get("toString")); // undefined ✓ clean
15
16// A Map does not turn into JSON directly
17JSON.stringify(m); // "{}" ← everything is lost
18JSON.stringify([...m]); // '[[1,"a"],["1","b"]]' ✓