成员或键值只能是对象
const weakset = new WeakSet()
weakset.add(1) // TypeError: Invalid value used in weak set
const weakmap = new WeakMap()
weakmap.set(1, {}) // TypeError: Invalid value used as weak map key
对成员或键值的引用是弱引用
什么是强引用?
let a = { age: 21 }
const set = new Set()
set.add(a)
a = null
console.log('set:',set) // set: Set { { age: 21 } }
a 对 { age: 21 } 这个值实际就是一个强引用。
set也存在一个对{ age: 21 }的强引用。当把a的引用释放后。通过set一样能查询到这个对象。
因为set存在对它的引用,导致GC并未回收它,它实际还存在于heap内存中。
弱引用的特点
let a = { age: 21 }
const weakset = new WeakSet()
weakset.add(a)
a = null
// 浏览器中无法手动操作GC,使用定时器等待一段时间让GC自动回收。
setTimeout(() => {
console.log('weakset:',weakset) // weakset: WeakSet { }
}, 5000)
可以看到弱引用实际不会被看成引用标记。当a的引用被释放后,等待下一次GC就会自动把{ age: 21 }这个对象回收。
WeakSet的出现让开发者在使用Set存储对象时无需当心内存泄漏的问题。WeakMap也是如此。只是有一点小差异,WeakMap仅仅只是针对于键值。
无法迭代
const weakset = new WeakSet()
const obj1 = {}
const obj2 = {}
weakset.add(obj1)
weakset.add(obj2)
// TypeError: weakset is not iterable
for(let v of weakset){
console.log(v)
}
基于弱引用和GC回收机制的原因, WeakSet 和 WeakMap 无法被迭代,因为GC是穿插在代码片段中运行的。很可能元素刚被遍历到,成员就取不到了。