关于JavaScript中的Set

147 阅读5分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第13天,点击查看活动详情

什么是Set

Set 是 ECMAScript 6 新增的一种新集合类型,它类似于数组,但是成员的值都是唯一的,没有重复的值,它为这门语言带来集合数据结构;

基本使用

Set 在很多方面都像是加强的 Map,因为他们大部分的 API 和行为都是共有的,所以如果看过我之前的文章 你知道JavaScript中的Map类型吗 应该也能很快的上手Set了

声明初始化
 const set = new Set();
 // Set(0) {size: 0}

初始化之后,可以使用 add()增加值,使用 has()查询,通过 size 取得元素数量,以及使用 delete()删除元素与 clear()清空集合;除了方法名有区别,具体使用与 Map 一致;

这里演示一下使用情况:

 const set1 = new Set([1, 2, 3, 3, 4, 5]);
 set1 // Set(5) { 1, 2, 3, 4, 5 }
 set1.size // 5
 set1.add(6) // Set(6) { 1, 2, 3, 4, 5, 6 }
 [...set1] //  [ 1, 2, 3, 4, 5 ]
 set1.clear(); // 清空
 set1.size // 0

Set特性

重复加入Set时,内部会使用“Same-value-zero equality” 算法来判断两个值是否不同,它类似于精确相等运算符(===),主要的区别是向 Set 加入值时认为NaN等于自身,而精确相等运算符认为 NaN 不等于自身;

 const set = new Set();
 const a = NaN;
 const b = NaN;
 set.add(a);
 set.add(b);
 // 只有一个NaN
 set // Set {NaN}

因此在使用.add API方法进行添加时会先判断是否存在,如果不存在就添加,存在就不添加;对于引用值而言,由于内存地址不同,即使是空对象会添加存储,不会进行去重操作,如下:

 const set = new Set([1, 2, 3, 5, 6, 7]);
 set.add(3).add(5).add(10).add({}).add({})
 // Set(8) { 1, 2, 3, 5, 6, 7, {}, {} }

由于 Set 成员的值都是唯一的,具有去重效果,因此非常适合用于数组或字符串去重

 const set = new Set([1,2,3,2,3]);
 // Set(3) {1, 2, 3}
 // 返回去重后的集合

Set 迭代操作

Set 会维护值插入时的顺序,因此支持按顺序迭代

集合实例可以提供一个迭代器(Iterator),能以插入顺序生成集合内容;

注意:Set 结构没有键名,只有键值(或者说键名和键值是同一个值)

Set 结构的实例有四个遍历方法,可以用于遍历成员:

  • Set.prototype.keys():返回键名的遍历器

     const set = new Set(['A', 'B', 'C']);
     for (const x of set.keys()) {
       console.log(x);
     }
     // A
     // B
     // C
    
  • Set.prototype.values():返回键值的遍历器

     const set = new Set(['A', 'B', 'C']);
     for (const x of set.values()) {
       console.log(x);
     }
     // A
     // B
     // C
    
  • Set.prototype.entries():返回键值对的遍历器

     for (let item of set.entries()) {
       console.log(item);
     }
     // ["A", "A"]
     // ["B", "B"]
     // ["C", "C"]
    
  • Set.prototype.forEach():使用回调函数遍历每个成员

    forEach方法的参数是一个处理函数;该函数的参数与数组的forEach一致,依次为键值、键名、集合本身 与 第二个参数用于绑定处理函数内部的this对象;

     let set = new Set([1, 4, 9]);
     set.forEach((value, key) => console.log(key + ' : ' + value))
     // 1 : 1
     // 4 : 4
     // 9 : 9
    

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

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

因此可以省略values方法,直接用for...of循环遍历 Set

 const set = new Set(['A', 'B', 'C']);
 for (const x of set) {
   console.log(x);
 }
 // A 
 // B 
 // C

Set 常用操作

使用展开运算符去除数组重复成员 :

 const array = [1, 2, 3, 4, 3, 2] 
 const setArr = [...new Set(array)] 
 // setArr: [1, 2, 3, 4]

使用Array.from方法将Set 集合转为数组

 const set = new Set([1, 2, 3, 4])
 const setArr = Array.from(set) 
 // setArr: [1, 2, 3, 4]

使用Set搭配 join 去除字符串重复成员:

 const str = 'asdwacawcas' 
 const setStr =  [...new Set()].join('')
 // 'asdwac'

Set 实现并集交集差集

初始化示例两个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}
通过将Set集合转换为数组并运用其filter方法筛选实现 交集
 let intersect = new Set([...a].filter(x => b.has(x)));
 // set {2, 3}
通过将Set集合转换为数组并运用其filter方法筛选实现 差集
 let difference = new Set([...a].filter(x => !b.has(x)));
 // Set {1}

Set实现规范

以下摘取自JavaScript高级程序设计第四版:

从各方面来看,Set 跟 Map 都很相似,只是 API 稍有调整;唯一需要强调的就是集合的 API 对自身的简单操作;很多开发者都喜欢使用 Set 操作,但需要手动实现:或者是子类化 Set,或者是定义一个实用函数库;要把两种方式合二为一,可以在子类上实现静态方法,然后在实例方法中使用这些静态方法;在实现这些操作时,需要考虑几个地方。

  • 某些 Set 操作是有关联性的,因此最好让实现的方法能支持处理任意多个集合实例。

  • Set 保留插入顺序,所有方法返回的集合必须保证顺序。

  • 尽可能高效地使用内存。扩展操作符的语法很简洁,但尽可能避免集合和数组间的相互转换能

    够节省对象初始化成本

  • 不要修改已有的集合实例

总结

Set是一个没有重复值的集合,默认可迭代,它没有键名,只有键值(迭代时键值相同);通常用于去重,可以轻松实现并集交集差集

最后如果本文对于本文有疑惑,还请指导勘正 (●'◡'●)