Set
(1)基本语法
Set是ES6新提供的一种数据结构,它类似于数组,但是成员的值都是唯一的,没有重复。
1.Set构造函数
Set本身是一个构造函数,Set函数接受具有iterator接口的数据结构作为参数,用来初始化。以数组为例:
const set=new Set([1,1,2,2,3,4,5,5]);
console.log(set);
//Set [1,2,3,4,5]
可以看出Set结构不会添加重复的值。
2. 去除数组重复成员的两种方法
用扩展运算符(…)(扩展运算符内部使用for…of循环)可以将Set结构转化为数组:
const set=new Set([1,1,2,2,3,4,5,5]);
let a=[...set];
console.log(a);
//[1,2,3,4,5]
利用Array.from方法(将一个类数组对象或者可遍历对象转换成一个真正的数组):
let a=[1,1,2,3,3,4,4,5];
let set = new Set(a);
console.log(set);
//Set [1,2,3,4,5]
let b=Array.from(set);
console.log(b);
//[1,2,3,4,5]
3. 向Set加入值不发生类型转换
向Set中加入值是是不发生类型转换的,所以5和“5”是两个不同的值:
const set=new Set(["5",5]);
console.log(set);
//Set ["5",5]
console.log(set.size);
//2
Set内部判断两个值是否相等用的算法是"Same-value equality",类似于全等操作符(====),主要的区别是NaN等于自身。
我们先来复习一下全等操作符:它只在两个操作数未经转换就相等的情况下返回true,需要注意的是:
console.log(null===undefined);
//false
console.log(NaN===NaN);
//false
console.log({"1":"1"}==={"1":"1"});
//false 两个对象内存地址不同,总是不全等的
console.log([1]===[1]);
//false 两个数组内存地址不同,总是不全等的
console.log({}==={});
//false 两个空对象内存地址不同,总是不全等的
console.log([]===[]);
//false 两个空数组内存地址不同,总是不全等的
let c=function(){console.log("1")};
let d=function(){console.log("1")};
console.log(c===d);
//false 两个函数内存地址不同,总是不全等的
let e=/at/g;
let f=/at/g;
console.log(e===f);
//false 两个正则表达式内存地址不同,总是不全等的
let date1=new Date();
let date2=new Date();
console.log(date1===date2);
//false 两个Date类型内存地址不同,总是不全等的
全等操作符进行比较时,对象(Date,Array,Obiect,RegExp,Function)通过指针指向的内存中的地址来做比较。
Set和全等操作符不同的地方在于,在Set内部认为NaN等于自身:
let set = new Set([NaN, NaN]);
console.log(set);
//Set [NaN]
console.log(set.size);
//1
Set和内存地址绑定,只要内存地址不同,就视为两个值:
在Set中两个对象,两个数组,两个函数,两个Date类型,两个正则表达式总是不相等的:
//对象
let set = new Set([{"1":"1"}, {"1":"1"}]);
console.log(set.size);
//2
//数组
let set1 = new Set([[1], [1]]);
console.log(set1.size);
//2
//空对象
let set3 = new Set([{}, {}]);
console.log(set3.size);
//2
//空数组
let set4 = new Set([[], []]);
console.log(set4.size);
//2
//正则表达式
let e=/at/g;
let f=/at/g;
let set5 = new Set([e, f]);
console.log(set5.size);
//2
//Date类型
let date1=new Date();
let date2=new Date();
let set6 = new Set([date1, date2]);
console.log(set6.size);
//2
//函数
let c=function(){console.log("1")};
let d=function(){console.log("1")};
let set7 = new Set([c, d]);
console.log(set7.size);
//2
因为对象(Date,Array,Obiect,RegExp,Function)通过指针指向的内存中的地址来做比较。
(2)操作方法
1.add(value)
添加某个值,返回Set结构本身。
由于add方法返回Set结构本身,所以可以采用链式写法:
let set = new Set();
set.add(1)
.add(2)
.add(3);
console.log(set);
// Set [1,2,3]
2.delete(value)
删除某个值,返回一个布尔值,表示删除是否成功。
3.has(value)
返回一个布尔值,表示参数是否为Set成员。
4.clear()
清除所有成员,没有返回值。
(3)遍历操作
Set的遍历顺序就是插入顺序,在Map中遍历顺序也是插入顺序。
1.keys():返回键名的遍历器。
2.values():返回键值的遍历器。
3.entries():返回键值对的遍历器。
由于Set结构没有键名,只有键值(或者说键名和键值是同一个值),所以keys方法和values方法的行为完全一致。
let set = new Set([1, 2, 3]);
for (let item of set.keys()) {
console.log(item)
}
//1
//2
//3
for (let item of set.values()) {
console.log(item)
}
//1
//2
//3
for (let item of set.entries()) {
console.log(item)
}
//[1,1]
//[2,2]
//[3,3]
for (let item of set) {
console.log(item)
}
//1
//2
//3
由上面第四个for…of循环可看出,Set结构的实例默认可遍历,默认遍历器生成函数就是它的values方法(在Map结构中默认遍历器接口是entries方法)。
4.forEach():使用回调函数遍历每个成员,没有返回值。
let set = new Set([1, 2, 3]);
set.forEach((value, key) => {
console.log(value * 2)
})
//2
//4
//6
遍历方法的应用
1.Set本身没有map和filter方法,可以转为数组,之后使用数组的map和filter方法:
let set = new Set([1, 2, 3]);
let set1=new Set([...set].map(x=>x*2));
console.log(set1);
//Set [2,4,6]
let set2=new Set([...set].filter(x=>(x%2===0)));
console.log(set2);
// Set [2]
2.实现并集、交集和差集:
let a = new Set([1, 2, 3]);
let b = new Set([4, 3, 2]);
//并集
let set1 = new Set([...a,...b]);
console.log(set1);
//Set [1,2,3,4]
//交集
let set2 = new Set([...a].filter(x => b.has(x)));
console.log(set2);
// Set [2,3]
//差集
let set3 = new Set([...a].filter(x => !b.has(x)));
console.log(set3);
// Set [1]