Set数据结构

1、Set

基本用法
ES6提供了新的数据结构Set。类似于数组,但是成员的值都是唯一的,没有重复的值。
Set本身是一个构造函数,用来生成Set数据结构。

const s = new Set();
[2,3,5,4,5,2,2].forEach(x=> s.add(x));
for (let i of s){
	console.log(i);
}// 2 3 5 4

上边代码通过add()方法向Set结构加入成员,结果表明Set结构不会添加重复的值。
Set函数可以接受一个数组作为参数,用来初始化。

const set = new Set([1,2,3,4,4]);
[...set]//[1,2,3,4]

Set实例的属性和方法
Set结构的实例有以下属性

  • Set.prototype.constructor:构造函数,默认就是Set函数
  • Set.prototype.size:返回Set实例的成员总数

Set实例的方法分为两大类:操作方法和遍历方法。四种操作方法为

  • Set.prototype.add(value):添加某个值,返回Set结构本身。
  • Set.prototype.delete(value):删除某个值,返回一个布尔值,表示删除是否成功。
  • Set.prototype.has(value):返回一个布尔值,表示该值是否为Set的成员。
  • Set.prototype.clear():清除所有成员,没有返回值

Array.from()方法可以将Set结构转为数组。
遍历操作
Set结构的实例有四个遍历方法,可以用于遍历成员

  • Set.prototype.keys():返回键名的遍历器
  • Set.prototype.values():返回键值的遍历器
  • Set.prototype.entries():返回键值对的遍历器
  • Set.prototype.forEach():使用回调函数遍历每个成员
    Set的遍历顺序就是插入顺序。
    (1)keys(),values(),entries()
    keys方法、values方法、entries方法返回的都是遍历器对象。由于Set结构没有键名,只有键值,所以keys方法和values方法的行为完全一致。
let set = new Set(['red','green','blue']);

for(let item of set.keys()){
	console.log(item);
}// red green blue
for(let item of set.values()){
	console.log(item);
}// red green blue
for(let item of set.entries()){
	console.log(item);
}
//["red","red"]

(2) forEach()
Set结构的实例与数组一样,也拥有forEach方法,用于对每个成员执行某种操作,没有返回值

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

forEach方法的参数就是一个处理函数。该函数与数组的forEach一致,依次为键值、键名、集合本身。
另外,forEach方法还可以有第二个参数,表示绑定处理函数内部的this对象。
(3)遍历的应用
拓展字符串内部使用for…of循环,所以也可以用于Set结构。

let set = new Set(['red','green','blue']);
let arr = [...set];

拓展运算符和Set结构相结合,就可以去除数组的重复成员。

let arr = [3,5,2,2,5,5];
let unique = [...new Set(arr)];//[3,5,2]

数组的map和filter方法也可以间接用于Set

let set = new Set([1,2,3]);
set = new Set([...set].map(x=>x*2));//返回set结构{2,4,6}

let set = new Set([1,2,3,4,5]);
set = new Set([...set].filter(x=>(x%2)==0));//返回set结构{2,4}

因此使用Set可以很容易地实现并集、交集和差集

let a = new Set([1,2,3]);
let b=  new Set([4,3,2]);
//并集
let union = new Set([...a,...b]);
//交集
let intersect = new Set([...a].filter(x => b.has(x)));
//a相对于b的差集
let difference = new Set([...a].filter(x=>!b.has(x)));

如果想在遍历操作中,同步改变原来的Set结构,目前没有直接的方法,有以下两种变通方法:1、利用原Set结构映射出一个新的结构,然后赋值给原来的Set结构。2、利用Array.from方法

//方法一
let set = new Set([1,2,3]);
set = new Set([...set].map(val => val*2));//{2,4,6}
//方法二
let set = new Set([1,2,3]);
set = new Set(Array.from(set,val => val*2));

2、WeakSet

Weakset结构与Set类似,也是不重复的值的集合。但是与Set有两个区别。
WeakSet的成员只能是对象,而不是其他类型的值。

const ws = new WeakSet();
ws.add(1)
// TypeError: Invalid value used in weak set
ws.add(Symbol())
// TypeError: invalid value used in weak set
//上边试图向WeakSet添加一个数值和Symbol值,结果报错,因为WeakSet只能放置对象

其次,WeakSet 中的对象都是弱引用,即垃圾回收机制不考虑 WeakSet 对该对象的引用,也就是说,如果其他对象都不再引用该对象,那么垃圾回收机制会自动回收该对象所占用的内存,不考虑该对象还存在于 WeakSet 之中。

这是因为垃圾回收机制根据对象的可达性(reachability)来判断回收,如果对象还能被访问到,垃圾回收机制就不会释放这块内存。结束使用该值之后,有时会忘记取消引用,导致内存无法释放,进而可能会引发内存泄漏。WeakSet 里面的引用,都不计入垃圾回收机制,所以就不存在这个问题。因此,WeakSet 适合临时存放一组对象,以及存放跟对象绑定的信息。只要这些对象在外部消失,它在 WeakSet 里面的引用就会自动消失。

由于上面这个特点,WeakSet 的成员是不适合引用的,因为它会随时消失。另外,由于 WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历。

这些特点同样适用于本章后面要介绍的 WeakMap 结构。
语法
WeakSet是一个构造函数,可以使用new命令,创建WeakSet数据结构

const ws = new WeakSet();

作为构造函数,WeakSet可以接受一个数组或者类似数组的对象作为参数。该数组的所有成员都会自动成为WeakSet实例对象的成员

const a = [[1,2],[3,4]];
const ws = new WeakSet(a);
//WeakSet {[1,2],[3,4]}

上边代码中,a是一个数组,有两个成员,也都是数组。将a作为WeakSet构造函数的参数,a的成员会自动成为WeakSet的成员。注意:是a数组的成员成为WeakSet的对象,而不是a数组本身,这意味着数组的成员只能是对象。

const b = [1,2];
const ws = new WeakSet(b);
// Uncaught TypeError: Invalid value used in weak set(…)

数组b的成员不是对象,加入WeakSet就会报错。
WeakSet的三个方法

  • WeakSet.prototype.add(value):向WeakSet实例添加一个新成员。
  • WeakSet.prototype.delete(value):删除WeakSet实例的指定成员。
  • WeakSet.prototype.has(value):返回一个布尔值,表示某个值是否存在
const ws = new WeakSet();
const obj = {};
const foo = {};

ws.add(window);
ws.add(obj);

ws.has(window);//true
ws.has(foo);//false

ws.delete(window);
ws.has(window);//false

WeakSet没有size属性,没有办法遍历它的成员。

ws.size // undefined
ws.forEach //undefined
ws.forEach(function(item){ console.log('WeakSet has ' + item)})
// TypeError: undefined is not a function

WeakSet不能遍历,因为成员都是弱引用,随时可能消失,遍历机制无法保证成员的存在,很可能刚刚遍历结束,成员就取不到了。WeakSet的一个用处是储存DOM节点,而不用担心这些节点从文档移除时会引发内存泄漏。

const foos = new WeakSet()
class Foo{
	constructor(){
		foos.add(this)
		}
	method(){
		if(!foos.has(this)){
			throw new TyprError('Foo.prototype.method 只能在Foo的实例上调用!');
			}
	}
}

上面代码保证了Foo的实例方法,只能在Foo的实例上调用。这里使用 WeakSet 的好处是,foos对实例的引用,不会被计入内存回收机制,所以删除实例的时候,不用考虑foos,也不会出现内存泄漏。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值