概念

Set对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set中的元素只会出现一次,即 Set 中的元素是唯一的。

NaNundefined都可以被存储在Set 中, NaN之间被视为相同的值(NaN被认为是相同的,尽管 NaN !== NaN)。

方法

创建一个Set对象

// 空Set
let mySet = new Set();

// 接收具有iterable接口的数据结构作为参数
let mySet1 = new Set([1,2,2,3,3,3])	// 1,2,3
let mySet2 = new Set('abbcccd')	// a,b,c

image-20200812181010595

size

// 值为整数,表示Set对象中有多少条目
mySet1.size() // 3

add

// 添加一个值
mySet.add(value);
// 可以链式调用
mySet.add(1).add(2);

has

// 返回一个布尔值来指示对应的值value是否存在Set对象中
mySet.has(value);

clear

// 清空Set
mySet.clear();

delete

var mySet = new Set();
mySet.add("foo");

mySet.delete("bar"); // 返回 false,不包含 "bar" 这个元素
mySet.delete("foo"); // 返回 true,删除成功

mySet.has("foo");    // 返回 false,"foo" 已经成功删除

entries

entries() 方法返回一个新的迭代器对象 ,这个对象的元素是类似 [value, value] 形式的数组,value 是集合对象中的每个元素,迭代器对象元素的顺序即集合对象中元素插入的顺序。由于集合对象不像 Map 对象那样拥有 key,然而,为了与 Map 对象的 API 形式保持一致,故使得每一个 entry 的 key 和 value 都拥有相同的值,因而最终返回一个 [value, value] 形式的数组。

var mySet = new Set();
mySet.add("foobar");
mySet.add(1);
mySet.add("baz");

var setIter = mySet.entries();

console.log(setIter.next().value); // ["foobar", "foobar"]
console.log(setIter.next().value); // [1, 1]
console.log(setIter.next().value); // ["baz", "baz"]

keys / values

Set 结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。

let mySet = new Set(['a','b','c']);

for (let item of mySet.keys()) {
  console.log(item);
}
// a
// b
// c

let setIter = mySet.keys() 

console.log(setIter.next().value); // "a"
console.log(setIter.next().value); // "b"
console.log(setIter.next().value); // "b"

Set 结构的实例默认可遍历,它的默认遍历器生成函数就是它的values方法。

Set.prototype[Symbol.iterator] === Set.prototype.values
// true

for (let x of mySet) {
    console.log(x);
}

forEach

Set 结构的实例与数组一样,forEach方法会依次为集合中的元素执行回调函数,没有返回值。

回调函数有三个参数:

  • 元素的值
  • 元素的索引
  • 正在遍历的集合对象

其中值与索引相同

mySet.forEach((value, key, myset) => {
    console.log(key + ' : ' + value)
})

应用

数组、字符串去重

let mySet = new Set([1, 1, 2, 2, 3, 3]);
let arr = [...set];

交集、并集、差集

let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);

// 并集
let union = new Set([...a, ...b]);
// Set {1, 2, 3, 4}

// 交集
let intersect = new Set([...a].filter(x => b.has(x)));
// set {2, 3}

// (a 相对于 b 的)差集
let difference = new Set([...a].filter(x => !b.has(x)));
// Set {1}

更改原来的Set结构

目前没有直接办法改变原有的Set结构

// 方法一
let set = new Set([1, 2, 3]);
set = new Set([...set].map(val => val * 2));
// set的值是2, 4, 6

// 方法二
let set = new Set([1, 2, 3]);
set = new Set(Array.from(set, val => val * 2));
// set的值是2, 4, 6

WeakSet

set中对象的引用无法被释放

let set1 = new Set(), obj = {};
set1.add(obj);
// 释放当前资源
obj = null;

obj被释放,但Set中对象并没有发生变化

image-20200912200623552

此时可以使用WeakSet

WeakSet的成员只能是对象,不能是其他类型的值。

WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

这是因为垃圾回收机制依赖引用计数,如果一个值的引用次数不为0,垃圾回收机制就不会释放这块内存。结束使用该值之后,有时会忘记取消引用,导致内存无法释放,进而可能会引发内存泄漏。WeakSet 里面的引用,都不计入垃圾回收机制,所以就不存在这个问题。因此,WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。

由于上面这个特点,WeakSet 的成员是不适合引用的,因为它会随时消失。另外,由于 WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历。

来源

https://es6.ruanyifeng.com/#docs/set-map


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

JS面向对象编程 上一篇
深度优先搜索与广度优先搜索 下一篇