写点什么

ES6 之 set 详解

作者:达摩
  • 2022 年 5 月 20 日
  • 本文字数:4519 字

    阅读完需:约 15 分钟

一直以来,JS 只能使用数组和对象保存多个书,缺乏像其他语言那样拥有丰富的集合类型。因此 ES6 新增了两种集合类型 set 和 map,用于在不同的场景发挥作用。

set 集合用于存放不重复的数据

1、如何创建 set 集合

new Set() // 创建一个没有任何内容的set集合
new Set(iterable) // 创建一个具有初始内容的set集合,内容来自于可迭代对象每一次迭代的结果
复制代码

Iterable object (可迭代对象)

例:创建没有内容的 set 集合

 const result = new Set(); console.log(result);
复制代码

执行结果:



例:创建一个可迭代对象的集合

 const result = new Set([1,3,3,4,5,5,6]); console.log(result);
复制代码

执行结果:


文章开头就提到 set 集合用于存放不重复的数据,所以最后输出结果就是不重复的数组,这也是一种非常简单的数组去重的方式。当然 set(数据)的参数不一定是数组,只要是可迭代的对象都可以,甚可以是字符串,如下:

 const result = new Set('abbccdde'); console.log(result);
复制代码

执行结果:



如果是字符串,它会先转换为 String 对象,String 对象其实就是可迭代对象,从执行结果来看 Set 集合不仅可以用于数组去重,也可以用于字符串去重。

2、如何对 set 集合进行后续操作

1、add(数据):添加一个数据到 set 集合末尾,如果数据已经存在,则不进行任何操作


例:使用 add 添加数据

  const result = new Set();        result.add(1);        result.add(2);        result.add(3);        result.add(1);//无效代码,Set内部会认为这条数据跟前面的数据重复        result.add(2);//无效代码,Set内部会认为这条数据跟前面的数据重复        console.log(result);
复制代码

执行结果:


这里有何细节大家注意一下:set 里面它是怎么判断两个数据是否相同呢?set 使用了 object.is 的方式判断两个数据是否相同,而不是用===严格相等,但是,针对+0 和-0,set 任务他们是相等的:

Object.is(+0,-0);//返回false
+0 === -0;//返回true
复制代码

本来 set 是使用 Object.is 方式来判断数据是否相等,但 set 单独针对这块又把+0 和-0 看成相等的。代码测试:

  const result = new Set();        result.add(1);        result.add(2);        result.add(3);        result.add(1);//无效代码,Set内部会认为这条数据跟前面的数据重复        result.add(2);//无效代码,Set内部会认为这条数据跟前面的数据重复        result.add(+0);        result.add(-0);//因此这段代码是无效的        console.log(result);
复制代码

执行结果:


本不应该出现这种情况的,之所以出现这个问题,纯粹是设计的问题,设计者估计是没办法开脱的,他不应该这样去设计,一个语言设计上它要统一。ES 的设计不可能是一个人完成的,是很多人一起设计开发的,难免会造成不统一的情况。


还有一点就是如果判断的数据是对象是否相等,那么就要判断它的地址是否相等。


2、has(数据):判断 set 中是否存在对应的数据

由于 set 集合中已经使用 add 添加好数据,那么我直接使用 has 方法


执行结果:



3、delete(数据):删除匹配的数据,返回是否删除成功


执行结果:



如果删除存在的数据就会返回 true,如果是删除不存在的数据,则返回 false


4、clear():没有参数,清空整个 set 集合


执行结果:



3、如何与数组进行转换

例:数组转换成 set 集合

//直接把数组放到 new Set(数组)里面就可以了const result = new Set(要转换的数组)
复制代码

例:set 集合转换为数组

//set本身也是一个可迭代对象,每次迭代的结果就是每一项的值const arr = [...result];
复制代码

接下来我们看一个小例子:要求就是把一个数组去重之后得到一个新的数组。


例:数组去重得到新的数组

var arr = [1,1,33,44,21,23,56,34,56,56,77,77];const result =[...new Set(arr)];console.log(result);
复制代码


执行结果:



以上就是非常简单地数组去重方法


顺便写下字符串去重:

var str = 'asndjfnajsknfdqwpsfdnsifnasnfdijsf';const result =[...new Set(str)].join(" ");console.log(result);
复制代码

执行结果:


4、如何遍历

刚才我们已经说了,set 本身是可迭代对象,因此可以用for of循环进行遍历。

  • 使用forof进行遍历

 const result = new Set([1,1,33,44,21,23,56,34,56,56,77,77]);
for (const item of result) { console.log(item);}
复制代码

执行结果:



  • 使用 set 中实例方法forEach

 const result = new Set([1,1,33,44,21,23,56,34,56,56,77,77]);
result.forEach(item => { console.log(item);})
复制代码


执行结果:


使用 forEach 遍历我们要注意,在数组中进行遍历的时候,forEach 是有三个参数的 forEach(item,index,that),第一个参数是每一项的值,第二个参数是下标,第三个参数是数组本身。但是在 set 里面是有差别的,我们加上三个参数试试:

 const result = new Set([1,1,33,44,21,23,56,34,56,56,77,77]);
result.forEach((item,index,that) => { console.log(item,index,that); })
复制代码

执行结果:


我们可以发现第一个参数和第三个参数没有问题,因为在数组中第一个参数也是每一项的值,第三个参数是数组本身,而第二个参数却不是下标,那为什么不是下标呢?


注意: set 集合中不存在下标,因为下标是数组特有的东西,不要认为是集合特有的东西,比如,对象也是集合,里面可以存多个数据,对象就没有下标。因此在 set 集合中是不可能获取下标的,那自然而然不可能用普通 for 循环去循环它的下标,如果说一定要用下标的话,可以先把 set 集合转换为数组再使用它的下标。由于 set 集合中不存在下标,那么第二个参数就不是下标了。


但 forEach 又要保持格式统一性,之所以要保持格式统一是因为我们有可能会写一些通用的回调函数既适合数组调用,又适合 set 集合,为了保证通用性,因此 set 集合中的 forEach 仍然会保留第二个参数,只不过第二个参数跟第一个参数是一样的,都表示集合中的每一项。


set 集合中不存在下标,因此 forEach 中的回调的第二个参数和第一个参数是一致的,均表示 set 中的每一项。


5、set 集合的应用

求两个数组的并集、交集、差集 (不能出现重复项),得到的结果是一个新数组


const arr1 = [22,33,55,33,11,5,6];const arr2 = [22,55,77,88,88,99];//方法一:const result = [...new Set(arr1.concat(arr2))];console.log("并集",result);
//方法二:const result = [...new Set([...arr1,...arr2])];console.log("并集:",result);
复制代码

执行结果:



例:求交集

const arr1 = [22,33,55,33,11,5,6];const arr2 = [22,55,77,5,88,99];//方法一:const s1 = new Set(arr1);const s2 = new Set(arr2);
const result = [...s1].filter(item => s2.has(item));console.log("交集:",result);
//方法二const s = new Set(arr1);const result = [...s].filter(item => arr2.indexOf(item) >=0 )console.log("交集:",result);
复制代码

执行结果:


例:求差集

const arr1 = [22,33,55,33,11,5,6];const arr2 = [22,55,77,5,88,99];//方法一const s1 = new Set(arr1);const s2 = new Set(arr2);
const result = new Set([...s1,...s2].filter(item => !s2.has(item) || !s1.has(item)));console.log("差集:",result);
//方法二const s1 = new Set(arr1);const s2 = new Set(arr2);
const result = [...s1,...s2].filter(item =>arr1.indexOf(item) >=0 && arr2.indexOf(item) <0 || arr2.indexOf(item) >=0 && arr1.indexOf(item)<0)console.log("差集:",result);
复制代码


执行结果:


6、手写 set 方法

我们手写的 set 方法跟浏览器提供的 set 对比的话肯定是不一样的,因为浏览器在实现 ES 标准的时候,它是可以调用底层资源的,比如说可以直接操作内存,它的效率要比我们手写的 set 方法效率高些。手写源码可以拓展我们的思维,仅此而已。


1、新建 mySet.js 文件


class MySet {    //构造函数里面参数可传可不传,给参数一个默认值就可以了    constructor(iterable = []) {        //验证传入的参数是否是可迭代对象,可迭代的对象类型一定是个对象        if (typeof iterable[Symbol.iterator] !== 'function') {            throw new TypeError(`${typeof iterator} ${iterator} is not iterable (cannot read property Symbol(Symbol.iterator))`)        }        this._datas = []; //由于不能操作底层内存,所以声明一个数组用于存放数据
//下面验证通过,迭代每一个可迭代的对象,把每一项放到 MySet里面去 for (const item of iterator) { //每迭代一次,把item加到 MySet里面去,问题转为封装add方法 this.add(item); } } //给 MySet添加数据 add(data) { //这里加数据的时候有个前提条件,就是重复的数据只放一次,问题转为封装has方法 if (!this.has(data)) { this._datas.push(data); } } //判断 MySet中是否存在对应的数据 has(data) { //这里判断是否有相同的值,问题转为封装isEqual方法 for (const item of this._datas) { if (this.isEqual(data, item)) { return true; } } return false; } //删除 MySet中对应的数据 delete(data) { for (let i = 0; i < this._datas.length; i++) { const element = this._datas[i]; if (this.isEqual(element, data)) { //删除 this._datas.splice(i, 1); return true; } } return false; } //清空整个数组 clear() { this._datas.length = 0; }
*[Symbol.iterator]() { for (const item of this._datas) { yield item; } } forEach(callback) { for (const item of this._datas) { callback(item, item, this); } } /** * 判断两个数据是否相等 * @param {*} data1 * @param {*} data2 */ isEqual(data1, data2) { //如果data1和data2都等于0,那么我们认为它们相等 if (data1 === 0 && data2 === 0) { return true; } return Object.is(data1, data2); }}
复制代码


代码测试:求差集


 <script src="js/mySet.js"></script>    <script >        const arr1 = [22,33,55,33,11,5,6];        const arr2 = [22,55,77,5,88,99];
const s1 = new MySet(arr1); const s2 = new MySet(arr2); const result = new MySet([...s1,...s2].filter(item => !s2.has(item) || !s1.has(item))); console.log("差集:",result); </script>
复制代码


执行结果:



代码测试:add、has、delete、clear

<script src="js/mySet.js"></script><script >       const result = new MySet();       result.add(1);       result.add(2);       result.add(3);       result.add(4);       console.log(result);</script>
复制代码



转载:https://xie.infoq.cn/article/fa813027c636a6dd09b3fef8e

发布于: 刚刚阅读数: 3
用户头像

达摩

关注

还未添加个人签名 2019.12.04 加入

还未添加个人简介

评论

发布
暂无评论
ES6 之set详解_js_达摩_InfoQ写作社区