快速了解 ES6 的Set与WeakSet

在 ECMAScript 6 之前,可以使用数组来存储值,而 ECMAScript 6 新增了 SetWeakSet 两种集合类型。

Set

Set 集合可以使用 new 来创建一个空集合:

const s = new Set();

也可以在创建 Set 时就初始化实例:

const s = new Set(["小赵", "小明", "小霞"]);

也可以在初始化 Set 实例时自定义迭代器。

const s = new Set({
    [Symbol.iterator]: function*() {
        yield "小赵";
        yield "小明";
        yield "小霞";
    }
});

可以使用 size 属性获取 Set 实例的数量。

初始化后,可以使用 has() 方法查询是否存在某元素:

s.has("小郑");    // false

可以使用 add() 方法添加元素:

s.add("小玲");

使用 delete() 方法删除元素,并返回一个 boolean 值,用来判断是否存在要删除的值:

s.delete("小明");    // true

使用 clear() 方法清空 Set 实例:

s.clear();

当然,SetMap 类似,可以包含任何 JavaScript 数据类型作为值,并且使用了 SameValeZero 操作,使用严格对象相等的标准来检查值的匹配性。

const s = new Set();
// SameValueZero 检查意味着独立的实例不会冲突
alert(s.has(function() {})); // false

Set 提供一个 Iterator 来以插入的顺序生成集合内容,通过 values() 方法以及其别名方法 keys() 方法获取迭代器,也可以通过 Symbol.iterator 属性来获取:

const s = new Set(["小赵", "小明", "小霞"]);
s.values === s[Symbol.iterator]    // true
s.keys === s[Symbol.iterator]    // true
for (let value of s.values()) {
    console.log(value);
}
// 小赵
// 小明
// 小霞
for (let value of s[Symbol.iterator]()) {
    console.log(value);
}
// 输出同上

也可以将集合转换为数组:

const s = new Set(["小赵", "小明", "小霞"]);
console.log([...s]); // ["小赵", "小明", "小霞"]

集合也提供了 entries() 方法,按照插入顺序返回所有元素的值的 [value, value] 数组,与 Map 相似,但这里的每个值的键和值相等。

也可以使用 forEach() 方法遍历集合:

const s = new Set(["小赵", "小明", "小霞"]);
s.forEach((v1, v2) => console.log(`${v1} -> ${v2}`));
// 小赵 -> 小赵
// 小明 -> 小明
// 小霞 -> 小霞

WeakSet

WeakSet 也是 ECMAScript 6 新增的集合,也是 Set 的子集,但同时,WeakSet 中的值没有被引用时,就会被 GC 回收。

这里,使用 new 关键字实例化空的 WeakSet

const ws = new WeakSet();

也可以在创建时就初始化,但是 WeakSet 的值只能是 Object 或者继承自 Object 的子类:

const val1 = {id: 1},
val2 = {id: 2},
val3 = {id: 3};
// 使用数组初始化弱集合
const ws1 = new WeakSet([val1, val2, val3]);

如果使用非对象的值会抛出 TypeError

const ws2 = new WeakSet([val1, "BADVAL", val3]);
// TypeError: Invalid value used in WeakSet
typeof ws2;

这里之所以限制只能使用 Object 作为值,是为了保证只有通过值对象的引用才能取得值。如果允许原始值,就没办法区分初始化时使用的字符串字面量和初始化之后使用的一个相等的字符串了。

初始化后的操作,也可以同 Set 一样,使用 add() 方法添加元素,has() 方法查询元素,delete() 方法删除元素。

弱值

WeakSetSet 不同之处在于集合中的值不被引用时,不会阻止 GC 回收。如下所示:

const ws = new WeakSet();
ws.add({});

add() 方法中添加一个新的元素,但是这个元素没有其它引用,所以该元素会被当作垃圾回收,成为一个空集合。但当 {} 元素被一个对象引用时,就不会被当作垃圾回收掉,如下所示:

const ws = new WeakSet();
const q = {v:{}};
ws.add(q.v);

上面的例子中,q.v 就不会成为 GC 回收的目标。但是,将最后一个引用摧毁掉,如将 q.v 指向 null,GC 就可以把这个值清理回收。

不可迭代值

WeakMap 一样,WeakSet 中的值在任何时候也可能会被销毁,所以没有提供其迭代的能力。因此,也就不会在不知道其对象引用的情况下从 WeakSet 中取到值。

应用

这里可以使用 WeakSet 将打上同一标签的元素存储起来,这样在以后的查询中就比较方便,如打上禁用标签的 DOM 元素都存储在 WeakSet 中:

const des = new WeakSet();
const lb = document.querySelector('#login');
// 通过加入对应集合,给这个节点打上“禁用”标签
des.add(lb);

这样,在 DOM 元素从 DOM 树中删除时,WeakSet 实例中的元素也会被 GC 回收,释放内存。

这里只所以没有使用 Set 是因为,在 DOM 树中删除后,Set 实例不会让 GC 回收该元素。

更多内容请关注公众号「海人为记
image

你可能感兴趣的