Map
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
Map
对象保存键值对,并且能够记住键的原始插入顺序。任何值(对象或者原始值)都可以作为键或值。
尝试一下
描述
键的相等
Object
和 Map
的比较
Object
和 Map
类似的是,它们都允许你按键存取一个值、删除键、检测一个键是否绑定了值。因此(并且也没有其他内建的替代方式了)过去我们一直都把对象当成 Map
使用。
不过 Map
和 Object
有一些重要的区别,在下列情况中使用 Map
会是更好的选择:
Map | Object | |
---|---|---|
意外的键 |
Map 默认不包含任何键。它只包含显式存入的键值对。
|
备注:这可以通过使用 |
安全性 |
Map 可以安全地与用户提供的键值一起使用。
|
在 |
键的类型 |
Map 的键可以为任何值(包括函数、对象或任何原始值)。
|
Object 的键必须为 String 或 Symbol 。
|
键的顺序 |
|
尽管现在普通的
该顺序最初仅在 ECMAScript 2015 中为自有属性定义;ECMAScript 2020 还定义了继承属性的顺序。但请注意,没有单一机制可以迭代对象的所有属性;各种机制各自包含不同的属性子集。( |
大小 |
Map 中的项目数量很容易从其 size 属性中获得。
|
确定 Object 中的项目数量通常更麻烦,效率也较低。一种常见的方法是通过获取 Object.keys() 返回的数组的长度。
|
迭代 |
Map 是可迭代对象,所以它可以直接迭代。
|
备注:
|
性能 |
在涉及频繁添加和删除键值对的场景中表现更好。 |
未针对频繁添加和删除键值对进行优化。 |
序列化和解析 |
没有对序列化或解析的原生支持。
(但你可以通过使用 |
原生支持使用 原生支持使用 |
设置对象属性
设置对象属性同样适用于 Map 对象,但容易造成困扰。
即,以下的代码能够正常运行(但不推荐):
const wrongMap = new Map();
wrongMap["bla"] = "blaa";
wrongMap["bla2"] = "blaaa2";
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
但这种设置属性的方式不会改变 Map 的数据结构。它使用的是通用对象的特性。'bla'
的值未被存储在 Map 中,无法被查询到。其他的对这一数据的操作也会失败:
wrongMap.has("bla"); // false
wrongMap.delete("bla"); // false
console.log(wrongMap); // Map { bla: 'blaa', bla2: 'blaaa2' }
正确的存储数据到 Map 中的方式是使用 set(key, value)
方法。
const contacts = new Map();
contacts.set("Jessie", { phone: "213-555-1234", address: "123 N 1st Ave" });
contacts.has("Jessie"); // true
contacts.get("Hilary"); // undefined
contacts.set("Hilary", { phone: "617-555-4321", address: "321 S 2nd St" });
contacts.get("Jessie"); // {phone: "213-555-1234", address: "123 N 1st Ave"}
contacts.delete("Raymond"); // false
contacts.delete("Jessie"); // true
console.log(contacts.size); // 1
类 Map 浏览器 API
浏览器类 Map 对象(或称为“maplike 对象”)是其行为在很多方面都类似于 Map
的 Web API 接口。
就像 Map
一样,对象中的条目可以以添加的顺序迭代。类似 Map
的对象和 Map
具有相同的属性和方法。但是,与 Map
不同的是,它们仅允许每个条目中的键和值具有特定预定义的类型。
允许的类型规范的 IDL 定义给出。例如,RTCStatsReport
是一个类似 Map
的对象,必须使用字符串作为键,对象作为值。这是在规范 IDL 中定义的:
interface RTCStatsReport {
readonly maplike<DOMString, object>;
};
类 Map
对象可以是只读的,也可以是可写的(参见上面 IDL 中的 readonly
关键字)。
- 只读的类
Map
对象具有size
属性,以及这些方法:entries()
、forEach()
、keys()
、values()
和[Symbol.iterator]()
。 - 可写的类
Map
对象还额外具有这些方法:clear()
、delete()
和set()
。
除了对键和值类型的限制外,其方法和属性的行为与 Map
中的对应实体相同。
以下是浏览器中只读的类 Map
对象的示例:
构造函数
Map()
-
创建
Map
对象。
静态属性
Map[Symbol.species]
-
用于创建派生对象的构造函数。
静态方法
Map.groupBy()
-
根据提供的回调函数返回的值将给定的可迭代对象分组。最终返回的
Map
对象使用测试函数返回的唯一值作为键,可用于获取每个组的元素数组。
实例属性
这些属性在 Map.prototype
上定义,并由所有 Map
实例共享。
Map.prototype.constructor
-
创建实例对象的构造函数。对于
Map
实例,初始值为Map
构造函数。 Map.prototype.size
-
返回
Map
对象中的键值对数量。 Map.prototype[Symbol.toStringTag]
-
[Symbol.toStringTag]
属性的初始值是字符串"Map"
。该属性在Object.prototype.toString()
中使用。
实例方法
Map.prototype.clear()
-
移除
Map
对象中所有的键值对。 Map.prototype.delete()
-
移除
Map
对象中指定的键值对,如果键值对存在并成功被移除,返回true
,否则返回false
。调用delete
后再调用map.has(key)
将返回false
。 Map.prototype.entries()
-
返回一个新的迭代器对象,其包含
Map
对象中所有键值对[key, value]
二元数组,以插入顺序排列。 Map.prototype.forEach()
-
以插入顺序为
Map
对象中的每个键值对调用一次callbackFn
。如果为forEach
提供了thisArg
参数,则它将作为每一次 callback 的this
值。 Map.prototype.get()
-
返回与指定的键
key
关联的值,若不存在关联的值,则返回undefined
。 Map.prototype.has()
-
返回一个布尔值,用来表明
Map
对象中是否存在与指定的键key
关联的值。 Map.prototype.keys()
-
返回一个新的迭代器对象,其包含
Map
对象中所有元素的键,以插入顺序排列。 Map.prototype.set()
-
在
Map
对象中设置与指定的键key
关联的值,并返回Map
对象。 Map.prototype.values()
-
返回一个新的迭代对象,其中包含
Map
对象中所有的值,并以插入Map
对象的顺序排列。 Map.prototype[Symbol.iterator]()
-
返回一个新的迭代器对象,其包含
Map
对象中所有元素[key, value]
二元数组,以插入顺序排列。
示例
使用 Map 对象
const myMap = new Map();
const keyString = "a string";
const keyObj = {};
const keyFunc = function () {};
// 添加键
myMap.set(keyString, "和键'a string'关联的值");
myMap.set(keyObj, "和键 keyObj 关联的值");
myMap.set(keyFunc, "和键 keyFunc 关联的值");
console.log(myMap.size); // 3
// 读取值
console.log(myMap.get(keyString)); // "和键'a string'关联的值"
console.log(myMap.get(keyObj)); // "和键 keyObj 关联的值"
console.log(myMap.get(keyFunc)); // "和键 keyFunc 关联的值"
console.log(myMap.get("a string")); // "和键'a string'关联的值",因为 keyString === 'a string'
console.log(myMap.get({})); // undefined,因为 keyObj !== {}
console.log(myMap.get(function () {})); // undefined,因为 keyFunc !== function () {}
将 NaN 作为 Map 的键
NaN
也可以作为键。虽然 NaN
与任何值甚至与自己都不相等(NaN !== NaN
返回 true),但是因为所有的 NaN
的值都是无法区分的,所以下面的例子成立:
const myMap = new Map();
myMap.set(NaN, "not a number");
myMap.get(NaN);
// "not a number"
const otherNaN = Number("foo");
myMap.get(otherNaN);
// "not a number"
使用 for...of 迭代 Map
Map
可以使用 for...of
循环来实现迭代:
const myMap = new Map();
myMap.set(0, "zero");
myMap.set(1, "one");
for (const [key, value] of myMap) {
console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one
for (const key of myMap.keys()) {
console.log(key);
}
// 0
// 1
for (const value of myMap.values()) {
console.log(value);
}
// zero
// one
for (const [key, value] of myMap.entries()) {
console.log(`${key} = ${value}`);
}
// 0 = zero
// 1 = one
使用 forEach() 迭代 Map
Map
也可以通过 forEach()
方法迭代:
myMap.forEach((value, key) => {
console.log(`${key} = ${value}`);
});
// 0 = zero
// 1 = one
Map 与数组对象的关系
const kvArray = [
["key1", "value1"],
["key2", "value2"],
];
// 使用常规的 Map 构造函数可以将一个二维的键值对数组转换成一个 Map 对象
const myMap = new Map(kvArray);
console.log(myMap.get("key1")); // "value1"
// 使用 Array.from 函数可以将一个 Map 对象转换成一个二维的键值对数组
console.log(Array.from(myMap)); // 输出和 kvArray 相同的数组
// 更简洁的方法来做如上同样的事情,使用展开运算符
console.log([...myMap]);
// 或者在键或者值的迭代器上使用 Array.from,进而得到只含有键或者值的数组
console.log(Array.from(myMap.keys())); // 输出 ["key1", "key2"]
复制或合并 Maps
Map
能像数组一样被复制:
const original = new Map([[1, "one"]]);
const clone = new Map(original);
console.log(clone.get(1)); // one
console.log(original === clone); // false. 浅比较 不为同一个对象的引用
备注: 请记住,数据本身未被克隆。
Map
对象间可以进行合并,但是会保持键的唯一性。
const first = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);
const second = new Map([
[1, "uno"],
[2, "dos"],
]);
// 合并两个 Map 对象时,如果有重复的键值,则后面的会覆盖前面的。
// 展开语法本质上是将 Map 对象转换成数组。
const merged = new Map([...first, ...second]);
console.log(merged.get(1)); // uno
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
Map
对象也能与数组合并:
const first = new Map([
[1, "one"],
[2, "two"],
[3, "three"],
]);
const second = new Map([
[1, "uno"],
[2, "dos"],
]);
// Map 对象同数组进行合并时,如果有重复的键值,则后面的会覆盖前面的。
const merged = new Map([...first, ...second, [1, "eins"]]);
console.log(merged.get(1)); // eins
console.log(merged.get(2)); // dos
console.log(merged.get(3)); // three
规范
Specification |
---|
ECMAScript Language Specification # sec-map-objects |
浏览器兼容性
BCD tables only load in the browser