Set与WeakSet

70 阅读3分钟

Set

Set 并不是数组,应该理解为集合
其内部的值是 无序的、唯一的\color{deeppink}{无序的、唯一的}

在ES6中,常用于【数组的去重】、【求交集】等。

Set的声明需要用new关键字。
参数一定是可遍历对象,否则会报错。

可遍历对象:
能够被 for,for in,forEach,Values,keys 等方法遍历的对象。
这是因为它们的原型上有[Symbol.iterator]方法,可以进行迭代。
{} 对象类型不是「可遍历对象」。

let s1 = new Set(1,2,3,4);
// 会报错,数字是不可迭代的
Uncaught TypeError: number 1 is not iterable (cannot read property Symbol(Symbol.iterator))

let s2 = new Set({name:'tom'});
// 同样报错
Uncaught TypeError: object is not iterable (cannot read property Symbol(Symbol.iterator))

「Set集合」中的元素是不重复的,多次添加同一个值,依旧只有一个。

let s3 = new Set([1, 2, 2, 'a', 'b', 'a', true, true, false]);
console.log(s3);
// Set(6) {1, 2, 'a', 'b', true, false}

let s4 = new Set([null, null, undefined, undefined, NaN, NaN,]);
console.log(s4);
// Set(3) {null, undefined, NaN}
// 按理说,NaN != NaN ,但是 Set 中作了处理,算同一个值

但是,多次添加「长相一致的引用类型」时,集合中是会出现多个的
这是因为,引用类型尽管长相一致,但它们的地址却不相同,本质上并不是同一个元素。

let s = new Set([ [1], [1], {name:'tom'}, {name:'tom'}, Symbol('ab'), Symbol('ab')]);
console.log(s);
// Set(6) {Array(1), Array(1), {…}, {…}, Symbol(ab), …}

从 Set 集合中取值

Set集合 是无序的,所以不存在 脚标、索引
自然是没有 set[0] 这种取值方式的。

方式一:转为数组再取值

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

let value = Array.from(mySet)[0]; // 取出第0个值 'a'
// 或
let value = [...mySet][0]; // 取出第0个值 'a'

方式二:将Set集合迭代

// 用 values()方法进行迭代
let mySet = new Set(['a', 'b',]);
let iterator = mySet.values();
let value = iterator.next().value; // 取出第0个值 'a'

// 用Set的forEach方法迭代
let value;
mySet.forEach((val, key) => {
  value = val;
  return false; // 只取出一个值,所以返回false停止迭代
});
console.log(value); // 取出第0个值 'a'

推荐第一种,转数组\color{red}{推荐第一种,转数组}

Set 常用方法、属性

delete()\color{orange}{delete()}
delete()方法用于删除集合中的元素,删除失败则返回false

let s = new Set([ 1, 'a', {name:'tom'}]);
console.log(s);
// Set(3) {1, 'a', {…}}

let result_1 = s.delete('a');
console.log(result_1);  // true
console.log(s);
// Set(2) {1, {…}}

let result_2 = s.delete({name:'tom'});
console.log(result_2);  // false
console.log(s);
// Set(2) {1, {…}}

从结果可以看到,元素'a'被删除了,但是元素 {name:'tom'} 却没有被删除。
因为 delete() 方法中的参数 {name:'tom'} 与集合中的元素 {name:'tom'} 虽然看起来一样,但地址并不相同。它们并不是同一个对象

Set中元素的遍历

Set集合原型上有[Symbol.iterator]方法,可以进行迭代的

Set集合 遍历出的 key 和 value 都是 value 的值。

let s = new Set(['a', 'b', {name:'tom'} ]);

// 可以用 for~of 对set集合的 values,keys,entries 方法进行遍历
for(let k of s.keys() ){
    console.log(k)
}
// 输出
a
b
{name: 'tom'}


for(let v of s.values() ){
    console.log(v)
}
// 输出
a
b
{name: 'tom'}
  

for(let [k,v] of s.entries() ){
    console.log(k, v)
}
// 输出
a a
b b
{name: 'tom'} {name: 'tom'}


// 也可以使用forEach进行遍历
s.forEach(function(v,k,i){
    console.log(arguments);
})
// 输出
Arguments(3) ['a', 'a', Set(3), callee: ƒ, Symbol(Symbol.iterator): ƒ]
Arguments(3) ['b', 'b', Set(3), callee: ƒ, Symbol(Symbol.iterator): ƒ]
Arguments(3) [{…}, {…}, Set(3), callee: ƒ, Symbol(Symbol.iterator): ƒ]

WeakSet

是一种特殊的 Set集合 ,内部元素自然也是不能重复的

有两处特殊

WeakSet中的元素必须是引用类型

let w_s_1 = new WeakSet([1,2,'a']);
// 会报错
Uncaught TypeError: Invalid value used in weak set

let w_s_2 = new WeakSet([ [1], {name:'tom'}, 2,'a']);

WeakSet 不能遍历

console.log(WeakSet.prototype)

WeakSet {Symbol(Symbol.toStringTag): 'WeakSet', delete: ƒ, has: ƒ, add: ƒ}
    add: ƒ add()
    constructor: ƒ WeakSet()
    delete: ƒ delete()
    has: ƒ has()
    Symbol(Symbol.toStringTag): "WeakSet"
    [[Prototype]]: Object

WeakSet原型上只挂载了 add delete has方法
没有size属性
也没有[Symbol.iterator]方法,所以不能遍历,
如 entries(),keys(),values(),forEach()等遍历方法也都不能用