重学JS | ES6既有Set,为啥还要有Weak Set?

809 阅读4分钟

这是我参与更文挑战的第21天,活动详情查看:更文挑战

[重学JavaScript系列文章连载中...]

先把标题问题解决了,再学习基础知识,知其前因后果。看段代码:

var obj = {a:1}
var set = new Set()
set.add(obj)
 // 移除原始引用
obj = null
// 输出:1,说明set中还是存在值,是不是null?
console.log(set.size()) 
// 取出值看看 输出:div#set,并不是null
[...set][0]  

例子中变量obj=null时,便清除了对初始对象的引用,但是Set集合确保留了这个引用,这里可以通过展开运算符获取到引用值。也是因为Set集合对obj引用的存在,当obj=null时,垃圾回收机制就不能释放初始变量obj指向的对象内存空间。倘若你想让其他引用不再存在时,Set集合中的这些引用也随之消息,就需要使用Weak Set集合,它是一种弱引用Set集合,只存储对象的弱引用,并且不可以存储原始值;集合中的弱引用如果是对象唯一的引用,则会被回收并释放相应内存。

var obj = {a:1}
var weakSet = new WeakSet()
weakSet.add(obj)
// 清除引用
obj = null
// {} weak set中的引用也被删除
console.log(weakSet) 

回答完了问题,再看看相应的使用方法。

Set集合

Set是一种有序列表,其中包含的是一些相互独立的非重复值,通过Set可以快速访问其中的数据,更有效的追踪各种离散值。

Set本身是一个构造函数,可以接受一个数组或者类数组对象作为参数。下面看看它的属性和函数:

属性

Set.prototype.constructor: 构造函数

Set.prototype.size: 返回实例的成员总数

方法

Set.prototype.add(value):添加一个值,返回Set结构本身

Set.prototype.delete(vallue):删除某个值,返回布尔值

Set.prototype.has(value):返回布尔值,表示是否是成员

Set.prototype.clear():清楚所有成员,无返回值

用法

  1. add详解

add添加新值,与原有值比较采用的是严格相等("==="),只有不等的才能添加到Set实例中。其中有个例外:NaN,因为NaN与NaN在严格相等比较下是不相等的,但Set里面只能有个NaN。

var set = new Set()
set.add(1)
set.add('1')
set.add(1)
// 1==='1' -> false
console.log(set) // Set(2) {1, "1"}

// NaN值
var set1 = new Set()
set1.add(NaN)
set1.add(NaN)
console.log(set1) // Set(1) {NaN} 添加了2个NaN,但输出只有1个

// 其他几种常规方法
var set = new Set([1,2,3,4])
set.delete(1) // true 删除成功,此时set:{2,3,4}
set.has(3)  // true 存在3
set.size()  // 3,删除了1个元素
set.clear() // 清除集合所有元素,set:{}
  1. 数组去重
// 一维数组去重
var arr = [1,1,2,2,3]
var set = new Set(arr)
console.log(set) // {1,2,3}

// 多数组合并去重
var arr1 = [1,2,3]
var arr2 = [1,2,3]
var set1 = new Set([...arr1,...arr2]) 
console.log(set1) // {1,2,3}
  1. Set与数组的转换
// 数组转为set
var arr = [1,2,3]
var set = new Set(arr)

// set转为数组:Array.from
var set = new Set()
set.add(1).add(2)
Array.from(set)  // [1,2]
// set转为数组:扩展运算符
var arr = [...set]  // [1,2]
  1. Set的遍历
//1. forEach
var set = new Set([1,3,'ha'])
set.forEach((item,idx)=>{
	console.log(item,idx)
})
//输出: 
// 1 1
// 3 3 3
// 3 ha ha
// 说明:传统forEach函数的第二个值是索引,但Set是键值对的集合,第二个参数表示键,实际上与第二个参数相同

// 2. keys 返回键名的遍历器
for(let item of set.keys()){
	console.log(item)
}

// 3. values 返回键值的遍历器
for(let item of set.values()){
  // Set的键和值是相等的,所以keys和values返回的值是一样的
	console.log(item) 
}
// 4. entries() 返回键值对的遍历器
for(let item of set.entries()){
	console.log(item) // 输出的是[key,value]
}

Weak Set集合

方法

支持add()、has()和delete()三个方法。例子如下:

var set = new WeakSet()
var obj = {a:1}
set.add(obj)  // 返回set实例 
set.has(obj)  // true
set.delete(obj) // true,此时set:{}
set.add(1) // 报错:TypeError: Invalid value used in weak set

其他

Weak Set集合不可迭代,不能被for-of循环。

Weak Set集合不暴露任何迭代器,所以无法通过程序本身检测其中内容

Week Set集合不支持forEach方法

Week Set集合不支持size属性。

Weak Set看似功能受限,实际上是为了让它能够正确的处理内存中的数据。

总结

至此学习了Set和Weak Set集合的使用,以及为啥有Weak Set集合。