作为 ES2015 的新增特性,Set
和 Map
对象大家应该很熟悉了,例如 Set
在数组去重等场景中经常会用到:
function unique(array = []) {
return Array.from(new Set(array));
}
但是一般我们都是只在需要这种数据结构的时候才去创建它,在用完之后就转回数组。大家可能都认为,相比 Set
、Map
对象,还是数组操作更熟悉一些。但实际上它们本身也提供了一些遍历方法,下面我们一起来看下。
Set 对象遍历操作
Set
结构的实例有四个遍历方法,可以用于遍历成员:
-
Set.prototype.keys()
:返回键名的遍历器 -
Set.prototype.values()
:返回键值的遍历器 -
Set.prototype.entries()
:返回键值对的遍历器 -
Set.prototype.forEach()
:使用回调函数遍历每个成员
需要特别指出的是,
Set
的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用Set
保存一个回调函数列表,调用时就能保证按照添加顺序调用。
与数组类似,Set
对象也提供 keys
、values
、entries
方法返回一个迭代器。与数组不同的是,Set
对象 没有键名,只有键值(或者说键名和键值是同一个值),所以 keys
方法和 values
方法的行为完全一致。
let set = new Set(['red', 'green', 'blue']);
for (let item of set.keys()) {
console.log(item);
}
// red
// green
// blue
for (let item of set.values()) {
console.log(item);
}
// red
// green
// blue
下面的代码中,entries
方法返回的遍历器,同时包括键名和键值,所以每次输出一个数组,它的两个成员完全相等:
let set = new Set(['red', 'green', 'blue']);
for (let item of set.entries()) {
console.log(item);
}
// ["red", "red"]
// ["green", "green"]
// ["blue", "blue"]
Set
结构的实例默认可遍历,它的默认遍历器生成函数就是它的 values
方法。
Set.prototype[Symbol.iterator] === Set.prototype.values
// true
一个对象只要实现了
Symbol.iterator
接口,就是一个可迭代对象,可以使用for...of
遍历,或者使用扩展运算符展开
这意味着,可以省略 values
方法,直接用 for...of
循环遍历 Set
。
let set = new Set(['red', 'green', 'blue']);
for (let x of set) {
console.log(x);
}
// red
// green
// blue
数组扩展运算符内部使用
for...of
循环,所以也可用于Set
结构
Set
结构的实例与数组一样,也拥有 forEach
方法,用于对每个成员执行某种操作,没有返回值。
et set = new Set([1, 4, 9]);
set.forEach((value, key) => console.log(key + ' : ' + value))
// 1 : 1
// 4 : 4
// 9 : 9
上面代码说明,forEach
方法的参数就是一个处理函数。该函数的参数与数组的 forEach
一致,依次为键值、键名、集合本身(上例省略了该参数)。这里需要注意,Set
结构的 键名就是键值(两者是同一个值),因此第一个参数与第二个参数的值永远都是一样的。
这里可以得出一个结论,
Set
对象forEach
的回调只需要接受一个形参即可
Map 对象遍历操作
Set
对象大家都知道使用扩展运算符转为数组去遍历,这样其实也没啥问题,但是 Map
对象的遍历就五花八门了,有的用扩展运算符转数组遍历,还有的用 Object.fromEntries
转为对象去遍历:
const map = new Map([["name", 1], ["age", 2]]);
// 方法一:转为数组
[...map].forEach(([key, val]) => {
// ...
})
// 方法二:转为对象
const obj = Object.fromEntries(map);
for (let key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
// ...
}
}
这边推荐使用第一种方法,直接遍历数组简单直接,不用担心遍历到对象原型上
实际上 Map
对象也提供了一些用于遍历的方法,下面我们来看下。
Map
结构原生提供三个遍历器生成函数和一个遍历方法。
-
Map.prototype.keys()
:返回键名的遍历器。 -
Map.prototype.values()
:返回键值的遍历器。 -
Map.prototype.entries()
:返回所有成员的遍历器。 -
Map.prototype.forEach()
:遍历 Map 的所有成员。
需要特别注意的是,Map 的遍历顺序就是插入顺序。
使用 keys
和 values
:
const map = new Map([
['F', 'no'],
['T', 'yes'],
]);
for (let key of map.keys()) {
console.log(key);
}
// "F"
// "T"
for (let value of map.values()) {
console.log(value);
}
// "no"
// "yes"
使用 entries
:
for (let item of map.entries()) {
console.log(item[0], item[1]);
}
// "F" "no"
// "T" "yes"
// 或者
for (let [key, value] of map.entries()) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
Map
结构的默认遍历器接口(Symbol.iterator
属性)是 entries
方法。
map[Symbol.iterator] === map.entries
// true
这意味着 Map
结构可以直接使用 for...of
遍历:
// 等同于使用map.entries()
for (let [key, value] of map) {
console.log(key, value);
}
// "F" "no"
// "T" "yes"
顺便提一下,用扩展运算符将 Map
结构转为数组,实际上就是 for...of
调用 entries
方法返回的迭代器:
const map = new Map([
[1, 'one'],
[2, 'two'],
[3, 'three'],
]);
[...map]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map.entries()]
// [[1,'one'], [2, 'two'], [3, 'three']]
[...map.keys()]
// [1, 2, 3]
[...map.values()]
// ['one', 'two', 'three']
此外,Map
还有一个 forEach
方法,与数组的 forEach
方法类似,也可以实现遍历。
map.forEach(function(value, key, map) {
console.log("Key: %s, Value: %s", key, value);
});
参考