🔂 JavaScript 映射与集合
# 映射 Map
Map
是一个 带键的数据项 的集合,就像一个 对象 Object
一样。 但是它们 最大的差别 是 Map
允许任何类型的键(key)。
🍎 包含以下方法和属性:
new Map()
:创建 一个map
。map.set(key, value)
:根据 键值 存储值。map.get(key)
:根据键 返回 值。如果map
不存在对应的key
,则返回undefined
。map.has(key)
:根据键 查找 值。如果key
存在则返回true
,否则返回false
。map.delete(key)
:根据键 删除 值。map.clear()
:清空 集合。map.size
:返回当前元素个数。
key
与对象不同,不会被转换成字符串,键可以是 任何类型。
注意在集合不建议使用方括号取值,这样会将
map
视为 JavaScript 的 plain object,因此它暗含了所有相应的限制(仅支持 string/symbol 键等)。
🌰 例子 / Map
可以使用 对象 作为键:
let john = {name: 'John'}
let visitsCountMap = new Map()
visitsCountMap.set(john, 'johnValue')
console.log(visitsCountMap.get(john)) // 'johnValue'
2
3
4
在对象中不能使用 对象 作为键。因为对象会先讲键转换为字符串,转换为字符串后,内容为
[object Object]
提示
在 Map
中 比较键: Map
使用 SameValueZero 算法比较键值是否相等。它和严格等于 ===
差不多,但区别是 NaN
被看成是等于 NaN
。所以 NaN
也可以被 用作键。这个算法不能被改变或者自定义。
提示
Map
的链式调用:每一次 map.set
调用都会返回 map
本身,所以可以进行链式调用:
map.set('1', 'str1')
.set(1, 'num1')
.set(true, 'bool1');
2
3
# Map
迭代
在 map
里使用 循环,可以使用以下三个方法:
map.keys()
:遍历并返回所有的 键。map.values()
:遍历并返回所有的 值。- 🍎
map.entries()
:遍历并返回 所有的实体[key, value]
,for..of
在默认情况下使用的就是这个。。
除此之外, Map
有内建的 forEach
方法,与 Array
类似。
🌰 例子 / Map
的三个遍历方法:
let recipeMap = new Map([
['cucumber', 500],
['tomatoes', 350],
['onion', 50]
])
for (let vegetable of recipeMap.keys()) {
console.log(vegetable)
}
for (let price of recipeMap.values()) {
console.log(price)
}
for (let entry of recipeMap.entries()) {
console.log(entry)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
:::
迭代的顺序与插入值的顺序相同。与普通的对象不同, Map
会保留插入值的顺序。
:::
🌰 例子 / 集合中的 forEach
方法:
recipeMap.forEach((value, key, map) => {
console.log(`${key}: ${value}`)
})
2
3
# 从对象中创建 Map Object.entries
当创建一个 Map
后,可以传入一个 带有键值对的数组(或 其它可迭代对象)来进行 初始化。
🌰 例子:
// 带有键值对 [key, value] 的数组
let map = new Map([
['1', 'str1'],
[1, 'num1'],
[true, 'bool1']
]);
2
3
4
5
6
如果想从一个已有的普通对象来创建一个 Map
,那么可以使用内建方法 Object.entries(obj) (opens new window),该方法返回 对象的键 / 值对数组,该数组格式完全按照 Map
所需的格式。
🌰 例子:
let obj = {
name: 'Simon',
age: 3
}
let map = new Map(Object.entries(obj))
for(let item of map.entries()){
console.log(item)
}
2
3
4
5
6
7
8
9
Object.entries
返回键 / 值对数组:[ ["name","John"], ["age", 30] ]
。
🌰 例子 / 从 Map
中转换值为对象
let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);
let obj = Object.fromEntries(map.entries())
console.log(obj)
2
3
4
5
6
7
Object.fromEntries
创建一个普通对象。调用map.entries()
将返回一个可迭代的键 / 值对,这刚好是Object.fromEntries
所需要的格式。可以简化为:
let obj = Object.fromEntries(map)
1因为
Object.fromEntries
期望得到一个可迭代对象作为参数,而不一定是数组。并且map
的标准迭代会返回跟map.entries()
一样的键 / 值对。因此,可以获得一个普通对象(plain object),其键 / 值对与
map
相同。
# 集合 set
Set
是一个特殊的类型集合「值的集合」(没有键),它的每一个值只能出现一次。
🍎 包含以下的方法:
new Set(iterable)
: 创建一个set
,如果提供了一个可迭代对象(通常是数组),将会从该对象里面 复制 值到set
中。set.add(value)
:添加value
到集合,返回集合本身。set.delete(value)
:删除value
。如果value
在这个方法调用的时候 存在则返回true
,否则返回false
。set.has(value)
:查找value
。存在返回true
,否则返回false
。set.clear()
:清空set
。set.size
:返回元素个数。
Set
的主要特点:重复使用同一个值调用 set.add(value)
并不会发生什么改变。这就是 Set
里面的 每一个值只出现一次 的原因。
🌰 例子 / 记录到访的客人,不重复记录已经来访的客人:
let set = new Set()
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);
console.log(set.size) // 3
for(let user of set){
console.log(user.name) // 3个
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
仅用数组实现在插入元素时查重时会带来遍历的时间开销。
Set
内部对唯一性检查进行了更好的优化。
# Set
迭代
可以使用 for..of
或 forEach
来遍历 Set:
🌰 例子 :
let set = new Set(["oranges", "apples", "bananas"]);
for (let item of set) {
console.log(item)
}
set.forEach((value, valueAgain, set)=>{
console.log(value)
})
2
3
4
5
6
7
8
9
forEach
的回调函数有三个参数:一个value
,然后是 同一个值valueAgain
,最后是目标对象。这三个参数是为了与
Map
兼容。对在特定情况下轻松地用Set
代替Map
很有帮助,反之亦然。
Map
中用于迭代的方法在 Set
中也同样支持:
set.keys()
:遍历并返回所有的值,set.values()
: 与set.keys()
作用相同,这是为了兼容Map
。set.entries()
:遍历并返回所有的实体,[value, value]
,它的存在也是为了兼容Map
。
# 总结
Map
是一个带键的数据项的集合。- 与普通对象
Object
的不同点:- 任何键、对象都可以作为键。
- 有其他的便捷方法,如
size
属性。
- 与普通对象
Set
是一组唯一值的集合。- 在
Map
和Set
中迭代总是按照值 插入的顺序 进行的,所以我们说这些集合是无序的,但是不能对元素进行重新排序,也不能直接按其编号来获取元素。