Set 和 Map 数据结构
1.Set
1)Set用法
类似于数组,但是成员的值都是唯一的,没有重复的值。Set本身是一个构造函数,用来生成 Set 数据结构。
const s = new Set();
[2, 3, 5, 4, 5, 2, 2].forEach(x => s.add(x));
for (let i of s) {
console.log(i);
}
// 2 3 5 4上面代码通过add()方法向 Set 结构加入成员,结果表明 Set 结构不会添加重复的值。
Set函数可以接受一个数组(或者具有 iterable 接口的其他数据结构)作为参数,用来初始化。
// 例一
const set = new Set([1, 2, 3, 4, 4]);
[...set]
// [1, 2, 3, 4]
// 例二
const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]);
items.size // 5
// 例三
const set = new Set(document.querySelectorAll('div'));
set.size // 56上面代码也展示了一种去除数组重复成员的方法。
向 Set 加入值的时候,不会发生类型转换,所以5和"5"是两个不同的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(===)
主要的区别是向 Set 加入值时认为NaN等于自身,而精确相等运算符认为NaN不等于自身。
2)Set 实例的属性和方法
Set.prototype.constructor:构造函数,默认就是Set函数。Set.prototype.size:返回Set实例的成员总数。
Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。
四个操作方法
Set.prototype.add(value):添加某个值,返回 Set 结构本身。Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。Set.prototype.clear():清除所有成员,没有返回值。
Array.from方法可以将 Set 结构转为数组。
这就提供了去除数组重复成员的另一种方法。
3)Set 实例的遍历操作
Set 结构的实例有四个遍历方法,可以用于遍历成员。
Set.prototype.keys():返回键名的遍历器Set.prototype.values():返回键值的遍历器Set.prototype.entries():返回键值对的遍历器Set.prototype.forEach():使用回调函数遍历每个成员
Set的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。
(1)keys(), values(), entries()
keys方法、values方法、entries方法返回的都是遍历器对象(详见《Iterator 对象》一章)。由于 Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。
这意味着,可以省略values方法,直接用for...of循环遍历 Set。
(2)forEach()
Set 结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值。
forEach方法还可以有第二个参数,表示绑定处理函数内部的this对象。
(3)遍历的应用
扩展运算符(...)内部使用for...of循环,所以也可以用于 Set 结构。
扩展运算符和 Set 结构相结合,就可以去除数组的重复成员。
数组的map和filter方法也可以间接用于 Set 了。
因此使用 Set 可以很容易地实现并集(Union)、交集(Intersect)和差集(Difference)。
如果想在遍历操作中,同步改变原来的 Set 结构,目前没有直接的方法,但有两种变通方法。一种是利用原 Set 结构映射出一个新的结构,然后赋值给原来的 Set 结构;另一种是利用Array.from方法。
2. WeakSet
1)WeakSet含义
WeakSet 结构与 Set 类似,也是不重复的值的集合。但是,它与 Set 有两个区别。
首先,WeakSet 的成员只能是对象,而不能是其他类型的值。
上面代码试图向 WeakSet 添加一个数值和Symbol值,结果报错,因为 WeakSet 只能放置对象。
其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。ES6 规定 WeakSet 不可遍历。
2)WeakSet语法
WeakSet 是一个构造函数,可以使用new命令,创建 WeakSet 数据结构。
注意,是a数组的成员成为 WeakSet 的成员,而不是a数组本身。这意味着,数组的成员只能是对象。
WeakSet 结构有以下三个方法。
WeakSet.prototype.add(value):向 WeakSet 实例添加一个新成员。
WeakSet.prototype.delete(value):清除 WeakSet 实例的指定成员。
WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否在 WeakSet 实例之中。
WeakSet 没有size属性,没有办法遍历它的成员。
3. Map
1)Map含义
类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。
上面代码使用 Map 结构的set方法,将对象o当作m的一个键,然后又使用get方法读取这个键,接着使用delete方法删除了这个键。
Map构造函数接受数组作为参数,实际上执行的是下面的算法。
事实上,不仅仅是数组,任何具有 Iterator 接口、且每个成员都是一个双元素的数组的数据结构(详见《Iterator》一章)都可以当作Map构造函数的参数。这就是说,Set和Map都可以用来生成新的 Map。
如果读取一个未知的键,则返回undefined。
注意,只有对同一个对象的引用,Map 结构才将其视为同一个键。这一点要非常小心。
同样的值的两个实例,在 Map 结构中被视为两个键。
Map 的键实际上是跟内存地址绑定的,只要内存地址不一样,就视为两个键。这就解决了同名属性碰撞(clash)的问题,我们扩展别人的库的时候,如果使用对象作为键名,就不用担心自己的属性与原作者的属性同名。
2)Map实例的属性和操作方法
(1)size属性
size属性返回 Map 结构的成员总数。
(2)Map.prototype.set(key,value)
set方法设置键名key对应的键值为value,然后返回整个 Map 结构。如果key已经有值,则键值会被更新,否则就新生成该键。
set方法返回的是当前的Map对象,因此可以采用链式写法。
(3)Map.prototype.get(key)
get方法读取key对应的键值,如果找不到key,返回undefined。
(4)Map.prototype.has(key)
has方法返回一个布尔值,表示某个键是否在当前 Map 对象之中。
(5)Map.prototype.delete(key)
delete方法删除某个键,返回true。如果删除失败,返回false。
(6)Map.prototype.clear()
clear方法清除所有成员,没有返回值。
3)遍历方法
Map 结构原生提供三个遍历器生成函数和一个遍历方法。
Map.prototype.keys():返回键名的遍历器。Map.prototype.values():返回键值的遍历器。Map.prototype.entries():返回所有成员的遍历器。Map.prototype.forEach():遍历 Map 的所有成员。
Map 结构转为数组结构,比较快速的方法是使用扩展运算符(...)。
4) 与其他数据结构的互相转换
(1)Map转为数组
Map 转为数组最方便的方法,就是使用扩展运算符(...)。
(2)数组转为 Map
将数组传入 Map 构造函数,就可以转为 Map。
(3)Map 转为对象
如果所有 Map 的键都是字符串,它可以无损地转为对象。如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。
(4) 对象转为Map
对象转为 Map 可以通过Object.entries()。
(5)Map转为 json
Map 转为 JSON 要区分两种情况。一种情况是,Map 的键名都是字符串,这时可以选择转为对象 JSON。
一种情况是,Map 的键名有非字符串,这时可以选择转为数组 JSON。
(6)JSON转Map
JSON 转为 Map,正常情况下,所有键名都是字符串。
4. WeakMap
1)WeakMap 含义
WeakMap结构与Map结构类似,也是用于生成键值对的集合。
2)WeakMap 用法
WeakMap只接受对象作为键名(null除外),不接受其他类型的值作为键名。
如果你要往对象上添加数据,又不想干扰垃圾回收机制,就可以使用 WeakMap。一个典型应用场景是,在网页的 DOM 元素上添加数据,就可以使用WeakMap结构。当该 DOM 元素被清除,其所对应的WeakMap记录就会自动被移除。
WeakMap的专用场合就是,它的键所对应的对象,可能会在将来消失。WeakMap结构有助于防止内存泄漏。
注意,WeakMap 弱引用的只是键名,而不是键值。键值依然是正常引用。
3)WeakMap 的语法
WeakMap 与 Map 在 API 上的区别主要是两个,一是没有遍历操作(即没有keys()、values()和entries()方法),也没有size属性。
WeakMap只有四个方法可用:get()、set()、has()、delete()。
4)WeakMap 用途
WeakMap 应用的典型场合就是 DOM 节点作为键名。下面是一个例子。
WeakMap 的另一个用处是部署私有属性。
Countdown类的两个内部属性_counter和_action,是实例的弱引用,所以如果删除实例,它们也就随之消失,不会造成内存泄漏。
最后更新于
这有帮助吗?