ECMAScript 6新增的Set是一种新的集合类型
,为这门语言带来集合数据结构。Set在很多方面都像是加强的Map,因为它们的大多数API和行为都是共有的。Set类似于数组,但是成员的值都是唯一的,没有重复值。
基本API
使用new关键字和Set构造函数可以创建一个空集合。
const m = new Set();
如果想在创建的同时初始化实例,则可以给Set构造函数传入一个可迭代对象,其中需要包含插入到新集合实例中的元素:
//使用数组初始化集合
const s1 = new Set(['xiaoming', 'xiaoli', 'xiaohong']);
console.log(s1.size); //3
//使用自定义迭代器初始化集合
const s2 = new Set({
[Symbol.iterator]:function*() {
yield 'xiaoming';
yield 'xioali';
yield 'xiaohong';
}
});
console.log(s2.size); //3
初始化之后,可以使用一些实例化方法。比如:add()
、has()
、delete()
、clear()
等等。注意:add()和delete()操作是幂等
的,因此不能使用链式编程在add()之后再进行delete()操作。
const m = new Set();
//1、使用add()增加值,返回的是Set结构本身
//使用链式编程可以将多个添加操作以及初始化连接起来 //const m = new Set().add('xiaoming');
m.add('xiaoming').add('xiaohong');
//2、使用size获取元素数量
console.log(m.size); //1
//3、使用delete()删除值,返回一个布尔值表示删除是否成功
console.log(m.delete('wangwu')); //false
//4、使用has()查询值,返回一个布尔值表示该值是否是Set的成员
console.log(m.has('wangwu')); //false
//5、使用clear()清除所有值,没有返回值
m.clear();
console.log(m.clear()); //undefined
与Map类似,Set可以包含任何JavaScript数据类型作为值。集合也使用SameValueZero
操作(ECMAScript内部定义,无法在语言中使用),基本上相当于使用严格对象相等的标准来检测值的匹配性。
const functionVal = function() {};
const symbolVal = Symbol();
const objectVal = new Object();
const s = new Set().add('functionVal'),add('symbolVal').add('objectVal');
console.log(s.has(objectVal); //true
console.log(s.has(symbolVal); //true
console.log(s.has(functionVal); //true
//SameValueZero检查意味着独立的实例不会冲突
console.log(s.has(function() {})); //false
另外,与严格相等一样,用作值的对象和其他“集合”类型在自己的内容或属性被修改时也不会改变。
const objVal = {}, arrVal = [];
const s = new Set().add(objVal).add(arrVal);
objVal.bar = "bar";
arrVal.push("bar");
console.log(s.has(objVal)); //true
console.log(s.has(arrVal)); //true
console.log(s);
顺序与迭代
Set会维护插入时的顺序,因此支持顺序迭代。集合实例可以提供一个迭代器(iterator),能以插入顺序生成集合内容。可以通过values()
方法及其别名方法keys()
(或者Symbol.iterator
属性,它引用values())取得这个迭代器:
const s = new Set(['zhangsan','lisi','wangwu']);
console.log(s.values === s[Symbol.iterator]); //true
console.log(s.keys === s[Symbol.iterator]); //true
for(let value of s.values()) { //等价于for(let value of s[Symbol.iterator] ()) {}
console.log(value); // zhangsan lisi wangwu
}
因为values()是默认迭代器,所以可以直接对集合实例使用扩展操作,把集合转换成数组:
//...s是ES6新特性的rest(剩余)参数
console.log([...s]); //['zhangsan','lisi','wangwu']
集合的entries()
方法返回一个迭代器,可以按照插入顺序产生包含两个元素的数组,这两个元素是每个集合中每个元素值的重复出现:
const s = new Set(['zhangsan','lisi','wangwu']);
for(let pair of s.entries()) {
console.log(pair); //['zhangsan','zhangsan'] ['lisi','lisi'] ['wangwu','wangwu']
}
Set结构的实例与数组一样,也拥有forEach
方法,用于对每个成员执行某个操作,没有返回值。如果不使用迭代器,而是使用回调函数,则可以调用集合的forEach()方法并传入回调,依次迭代每个键值对。传入的回调接收可选的第二个参数,这个参数用于重写回调内部this值:
const s = new Set(['zhangsan','lisi','wangwu']);
s.forEach((val, dupVal) => console.log(`${val} -> ${dupVal}`)); //zhangsan -> zhangsan lisi -> lisi wangwu -> wangwu
修改集合中值的属性不会影响其作为集合值的身份:
const s = new Set(['zhangsan']);
//字符串原始值作为值不会被修改
for(let value of s.values()) {
value = 'newVal';
console.log(value); //newVal
console.log(s.has('zhangsan')); //true
}
const valObj = {id: 1};
const s2 = new Set([valObj]);
//修改值对象的属性,但对象仍然处于集合中
for(let value of s2.values()) {
value.id = 'newVal';
console.log(value); //{id: 'newVal'}
console.log(s.has(valObj)); //true
}
console.log(valObj); // {id: 'newVal'}
定义正式集合操作
Set和Map在许多方面都非常相似,只是它们的API稍有调整。另外,集合的API只支持自引用操作。在实现这些操作时,要考虑到:①某些Set操作是有关联性的,因此最好让实现的方法能支持处理任意多个集合实例;②Set保留插入顺序,因此所有方法返回的集合必须保证集合;③不要修改已有的集合实例。
class XSet extends Set {
union(...sets) {
return XSet.union(this,...sets);
}
intersection(...sets) {
return XSet.intersection(this,...sets);
}
difference(set) {
return XSet.difference(this,set);
}
symmetricDifference(set) {
return XSet.symmetricDifference(this,set);
}
cartesianProduct(set) {
return XSet.cartesianProduct(this,set);
}
powerSet() {
return XSet.powerSet(this);
}
//返回两个或更多集合的并集
static union(a,...bSets) {
const unionSet = new XSet(a);
for(const b of bSets) {
for(const bValue of b) {
unionSet.add(bValue);
}
}
return unionSet;
}
//返回两个或更多集合的交集
static intersection(a,...bSets) {
const intersectionSet = new XSet(a);
for(const aValue of intersectionSets) {
for(const b of bSets) {
if(!b.has(aValue){
intersectionSet.delete(aValue);
}
}
}
return intersectionSet;
}
//返回两个集合的差集
static difference(a,b) {
const differenceSet = new XSet(a);
for(const bValue of b) {
if(a.has(bValue){
differenceSet.delete(bValue);
}
}
return differenceSet;
}
//返回两个集合的对称差集
static symmetricDifference(a,b) {
return a.union(b).difference(a.intersection(b));
}
//返回两个集合(数组对形式)的笛卡尔积
//必须返回数组集合,因为笛卡尔积可能包含相同值的对
static cartesianProduct(a,b) {
const cartesianProductSet = new XSet();
for(const aValue of a) {
for(const bValue of b) {
cartesianProductSet.add([aValue,bValue]);
}
}
return cartesianProductSet;
}
//返回一个集合的幂集
static powerSet(a) {
const powerSet = new XSet().add(new XSet());
for(const aValue of a) {
for(const set of new XSet(powerSet) {
powerSet.add(new XSet(set).add(aValue));
}
}
return powerSet;
}