weakmap,weakset,map,set的理解

216 阅读4分钟

1. set

类似数组,但是成员唯一,没有重复值,可以用来做数组去重(常见),其中的值是精确值,1和"1"不同 可以接受数组作为构造参数。可迭代。

属性

size: 成员数

方法

add: 添加值,并返回set本身 delete:删除值,通过value删除,返回boolean判断是否成功 has:返回boolean,判断是否在set中 clean:清楚所有成员

遍历方法

keys, values, entries, for of(不能使用for in), forEach

2. weakset

类似set,但是成员只能是对象(null除外),不可迭代,不可清空 没有size属性,没办法遍历成员, 所有的成员都是弱引用,随时可能消失,便利机制无法保证成员的存在,所以不能遍历,但是有一个用处,就是储存dom节点,不用担心这些节点从文档移除的时候,会引发内存泄漏或业务场景问题。

3. Map

类似object,但是可以用任何值作为一个键名,类似于值---值这样的对应概念,是一种更完善的hash结构,更适合作为数据映射。可迭代

方法

get:获取对应value set:设置对应key-value has:判断是否有 delete:删除指定key

遍历方法

几乎同set,但是遍历顺序是插入顺序,map也能转数组

4. weakMap

与map结构类似,但是键名必须是对象(null除外),不可迭代,不可清空 它的设计目的在于,想在某些对象上储值,但是这会形成对象应用,改变原对象,如下

const e1 = document.getElementById('foo');
const e2 = document.getElementById('bar');
const arr = [
  [e1, 'foo 元素'],
  [e2, 'bar 元素'],
];
// e1,e1被arr引用了,一旦不需要这两个对象,就需要手动删除引用
// 删除
arr[0] = null
arr[1] = null
// 这样不方便,如果忘了删就会引起内存泄漏

weakMap键名弱引用,只要所引用的对象的其他引用被清除,gc就会回收对象内存,也就是说,一旦不需要,weakmap对应键值对就能自动消失,无需手动删除。 例如挂在dom元素上的数据,dom元素消失的时候,对应weakmap键值对纪录也会自动移除,释放内存

// 使用场景
var arr = [1,2,3]
var wm = new WeakMap()
wm.set(arr, 22)
// 直接清除arr
arr= null
// 此时weakmap没有阻止垃圾的回收,自动也删除了对应键值

但是这个所谓的移除,在浏览器上去实现的话,你还是能看到的 image.png

所以更好的方式,是在nodejs环境去查看内存是否被回收,

node --expose-gc # 开启手动回收垃圾机制
// 手动执行一次垃圾回收,保证获取的内存使用状态准确
> global.gc();
undefined
 
// 查看内存占用的初始状态,heapUsed 为 4M 左右
> process.memoryUsage();
{ rss: 21106688,
  heapTotal: 7376896,
  heapUsed: 4153936,
  external: 9059 }
 
> let wm = new WeakMap();
undefined
 
// 新建一个变量 key,指向一个 5*1024*1024 的数组
> let key = new Array(5 * 1024 * 1024);
undefined
 
// 设置 WeakMap 实例的键名,也指向 key 数组
// 这时,key 数组实际被引用了两次,
// 变量 key 引用一次,WeakMap 的键名引用了第二次
// 但是,WeakMap 是弱引用,对于引擎来说,引用计数还是1
> wm.set(key, 1);
WeakMap {}
 
> global.gc();
undefined
 
// 这时内存占用 heapUsed 增加到 45M 了
> process.memoryUsage();
{ rss: 67538944,
  heapTotal: 7376896,
  heapUsed: 45782816,
  external: 8945 }
 
// 清除变量 key 对数组的引用,
// 但没有手动清除 WeakMap 实例的键名对数组的引用
> key = null;
null
 
// 再次执行垃圾回收
> global.gc();
undefined
 
// 内存占用 heapUsed 变回 4M 左右,
// 可以看到 WeakMap 的键名引用没有阻止 gc 对内存的回收
> process.memoryUsage();
{ rss: 20639744,
  heapTotal: 8425472,
  heapUsed: 3979792,
  external: 8956 }

react中的事件绑定就是用这个实现的

5. 是否可迭代

for in 可以作用于数组(获取下标),对象(获取key),但是不能作用于map,set,weakSet,weakMap,但是使用时不会报错。 for of 可以作用数组(获取value),map(item会变成键值对数组),set(获取value),不能作用于weakMap,weakSet,对象,原因都是他们是不可迭代的,会报错

weak的设计主要目的

是为了解决内存泄漏的问题,保证垃圾回收机制能准确回收用不到的变量,例如map的键是一个很大的dom对象,然后dom没了,这时候,dom变量的空间无法回收,因为被map的其中key所占用了,造成内存泄漏,但是如果是weakmap,就不会阻止垃圾回收机制。 ​ ​ 参考: 介绍下 Set、Map、WeakSet 和 WeakMap 的区别