深入理解ES6新特性之Set结构

  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;
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值