Set Weakset解析

字数 3846 阅读 362 喜欢 0

Set Weakset解析

众所周知,数组一直是js中的一种集合类型(当然,非数组对象也算是一种集合,只不过这种集合是键值对的集合,所以对象和数组的用途也是完全不同的)
再后来的发展中,大家发现其实我们需要一种用非数组对象创建的一种数据结构,这种结构在ES6中被称为Set集合与Map集合。

其实在最开始还没Set集合与Map集合这种概念的时候,我们开发者就有类似的实现方式了

var set = Object.create(null)

set.flag = true

if (set.flag) {
  // do something
}

在早期我们会定一个一个原型为null的对象,不继承任何属性。用这种类似的方式去判断对象中的某个属性值是否存在。

其实Map集合与Set集合两者的区别点在于存储的值不同

var map = Object.create(null)

map.name = 'xiaoming'

var person = map.name
console.log(person)

// 'xiaoming'

当我们的程序简单的时候我们完全可以像上面的写法一样,通过创建原型为null的对象来实现这种操作,但是当我们程序变得复杂的时候,我们可能就不会那么好实现了:

var map = Object.create(null)

map[5] = 'apple'
console.log(map[5])
// 'apple'

上面的程序在解析的过程中会把我们数字类型的5,解析称string类型的‘5’,这样的话,其实我们最初的目的是没有达到的,类似的情况还有像

var map = Object.create(null)
var a = {}
var b = {}

map[a] = 'apple'

console.log(map[b])
// 'apple'

可能有同学会问,这是为啥呢,其实原因很简单,在解析的过程中map[a],被解析成了map[object],所以导致map[b]的结果等于map[a]
所以因为这些问题的出现,在ES6中添加了Map集合与Set集合

Set集合

Set类型是一种有序列表,其中每一项都是相互独立的并且是不重复的,通过Set集合我们可以快速的访问其中的数据,有效的追踪各种离散值。

就和上面的概念一样,每一项都是先对独立的并且是不重复的!!! 说到这里大家可能和我像的一样,去重!!!没错使用Set集合可以很快的对一个数组进行去重!!

 function unique(arr) {
    return [...new Set(arr)]
 }
 // 或者
 function unique1(arr) {
    return Array.from(new Set(arr))
}

api

  1. add() 向集合中添加元素
  2. size() 获取集合中目前元素的数量
  3. has() 判断集合中是否存在某个值
  4. delete() 删除集合中的某一个值
  5. clear() 移除集合中所有的值
  6. forEash()
let set1 = new Set()
set1.add(5)
set1.add('5')
console.log(set1.size)
// 2
set1.add(5)
console.log(set1.size)
// 2

let set2 = new Set()
let a = {}
let b = {}
set2.add(a)
set2.add(b)
console.log(set2.size)
// 2
// set2中的a和b不会被转换成字符串,因此他们在集合中还是两个独立的元素

let set3 = new Set()

set3.add(1)
set3.add(2)

set3.delete(1)
console.log(set3.size)
// 1

set3.add(4)
set3.clear()
set3.has(1) // false
set3.size // 0

下面大家看一个比较有意思的例子

set1.has(5)

set2.has({})

大家可以看一下上面的这两个值分别是什么,为什么?😄

forEach

数组中的forEach相信大家都比较熟悉,回调函数里面有三个参数
forEach

在Set集合中forEach的回调函数也有三个参数

  • 当前元素
  • 与第一个参数相同
  • 当前Set集合

其实理解起来可能会有点别扭,为啥第一个参数和第二个参数表示同一个值!? 其实在Map集合和数组中forEach的回调函数都接受三个值,所以为了避免Set、Map、数组之间forEach这个api的歧义过大,最终ECMAScript6标准指定委员会决定让Set的forEach这个api与其他的保持一致,也接受三个参数。

在Set集合的forEach()方法中,第二个参数和数组是一样的this。当然我们在写代码的过程中也可以使用=> (箭头函数)来代替传递this的这种操作。

Set集合中还有keys()values()两个api,返回一个新的迭代器对象,该对象包含Set对象中的按插入顺序排列的所有元素的值。

https://static.jinhaidi.cn/images/20191019210810.png?jhd_blog_image

WeakSet集合

由于在Set的实例中存储对象于存储在变量中完全一样,所以只要Set实例存在,那么垃圾回收机制就不能释放该对象的内存空间,因此Set类型又可以被看作是一种强类型的Set集合

举个栗子:在web页面中通过js代码记录了一些DOM元素,这些元素有可能呗另外一段代码或者脚本移除,但是你又不想自己的代码保留对DOM元素的最后引用,也就是咱们经常说的内存泄漏的问题!
为了解决这个问题,ECMAScript6中引入了一种弱类型的Set集合 WeakSet集合

WeakSet支持add,clear,delete,has四个api

var ws = new WeakSet();
var obj = {};
var foo = {};

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

ws.has(window); // true
ws.has(foo);    // false, 对象 foo 并没有被添加进 ws 中 

ws.delete(window); // 从集合中删除 window 对象
ws.has(window);    // false, window 对象已经被删除了

ws.clear(); // 清空整个 WeakSet 对象

区别

需要注意的Set和WeakSet两者很大的区别,一方面WeakSet是只能保存对象值的弱引用,另外还有下面几个差别:

  1. 对于 WeakSet 的实例,若调用add()方法时传入了非对象的参数,就会抛出错误(has()或delete()则会在传入了非对象的参数时返回false);
  2. Weak Set 不可迭代,因此不能被用在 for-of 循环中;
  3. Weak Set 无法暴露出任何迭代器; (例如 keys()与values() 方法,因此没有任何编程手段可用于判断 Weak Set 的内容);
  4. Weak Set 没有 forEach() 方法;
  5. Weak Set 没有 size 属性

WeakSet之所以不可遍历是由于WeakSet 内部有多少个成员,取决于垃圾回收机制有没有运行,运行前后很可能成员个数是不一样的,而垃圾回收机制何时运行是不可预测的,因此 ES6 规定 WeakSet 不可遍历。

虽然WeakSet集合的功能看似受限,其实是为了让它能够正确的处理内存中的数据。所以,当你只需要追踪对象引用,你就应该是使用WeakSet集合而不是Set集合。