JavaScript Map:键值存储与高效查找

JavaScript Map 对象用于存储键值对,并允许任意值(对象、函数、原始值)作为键。需要按插入顺序迭代、使用非字符串键,或频繁新增和删除条目时,应使用 Map。与普通对象不同,Map 的键不会受到继承原型属性影响,这能避免动态字符串键带来的意外冲突。

JavaScript Map 键值存储示例

输出:

输出将显示在这里...

输出:

95
false
2

这个示例如何工作

  1. new Map() 创建一个空映射。直接调用 Map() 而不加 new 会抛出 TypeError。
  2. set 添加键值对并返回 Map 实例,因此可以链式调用,例如 map.set("a", 1).set("b", 2)。对已有键再次调用 set 会覆盖值,但不会改变该键在迭代中的位置。
  3. get 返回已存储的值;如果键不存在,返回 undefined。当你要区分“键不存在”和“键存在但值是 undefined”时,使用 has。
  4. size 表示当前条目数量;对象则需要 Object.keys(obj).length 才能得到数量。

JavaScript Map 的常见错误

使用方括号语法而不是 set/get:

错误写法:

const map = new Map();
map["键"] = "值";
console.log(map.get("键")); // undefined

正确写法:

const map = new Map();
map.set("键", "值");
console.log(map.get("键")); // "值"

方括号语法是在 Map 实例上设置对象属性,而不是设置 Map 条目。get、has 和 size 都不会读取这个属性。

误以为对象键按结构相等比较:

错误写法:

const map = new Map();
map.set({id: 1}, "小李");
console.log(map.get({id: 1})); // undefined

正确写法:

const key = {id: 1};
map.set(key, "小李");
console.log(map.get(key)); // "小李"

Map 使用 SameValueZero 进行键相等比较。对对象来说,这意味着按引用身份比较:两个看起来相同的对象字面量仍然是不同的键。

JavaScript Map 与 Object 对比

MapObject
键类型任意值(对象、函数、原始值)仅字符串和 Symbol
迭代顺序插入顺序(有保证)大多按插入顺序,但类整数键会先排序
大小map.sizeObject.keys(obj).length
原型冲突无键可能遮蔽 toString、constructor 等
JSON 序列化需手动通过 Object.fromEntries内置 JSON.stringify

当键不是字符串、条目需要频繁增删,或你要存储不受信任的键时,使用 Map。固定结构记录或需要 JSON 序列化的数据,使用普通对象。 如果你需要对象但又想避免原型键冲突,可用 Object.create(null) 作为底层对象。

FAQ

JavaScript Map 会保留插入顺序吗?

会。Map 会按键首次插入的顺序迭代条目。对已有键调用 set() 只会更新值,不会改变原位置。删除后再添加同一个键,会把它放到末尾。

JavaScript Map 可以使用对象作为键吗?

可以,但 Map 比较对象键时看的是引用而不是结构。属性完全相同的两个不同对象会被当成两个键。把对象引用保存到变量里,并在 set 和 get 中使用同一个引用。

如何把 JavaScript Map 转成对象或 JSON?

调用 Object.fromEntries(map) 可以创建普通对象,但前提是所有键都是字符串或 Symbol。若有非字符串键,先序列化条目数组:JSON.stringify([...map])。

JavaScript Map 和 JavaScript map() 函数有什么区别?

Map 是用于存取数据的键值集合。JavaScript map() 函数(Array.prototype.map)是数组方法,会对每个元素应用回调并返回新数组。两者同名,但用途完全不同。