在看vue3源码的时候,看到WeakMap和WeakSet,不是很明白,平时只用Set做数组去重。所以趁这个机会好好学一下,查缺补漏。
Set 和 Map 主要的应用场景在于 数据重组 和 数据储存
Set 是一种叫做集合的数据结构,Map 是一种叫做字典的数据结构
Map
Map 区别与Object的最大的特点是,它能存储任意类型的key。
举个例子:
let map = new Map()
let john = { name: 'John' }
map.set('1', 'str1') // a string key
map.set(1, 'num1') // a numeric key
map.set(true, 'bool1') // a boolean key
map.set(john, 123) // a object key
console.log(map.get(1)) // 'num1'
console.log(map.get('1')) // 'str1'
console.log(map.get(true)) // 'bool1'
console.log(map.get(john)) // 123
console.log(map.size) // 4
Map会保留key的类型,而Object会把key转换为string(Object的key还可以是Symbol)。
属性和操作方法:
size属性:返回Map结构的成员总数Map.prototype.set(key, value):向Map中添加或更新元素Map.prototype.get(key):读取key对应的键值,如果找不到key,返回undefinedMap.prototype.has(key):判断Map对象中是否有Key所对应的值,有返回true,否则返回falseMap.prototype.delete(key):delete方法删除某个键,返回true。如果删除失败,返回falseMap.prototype.clear():清除所有成员,没有返回值
遍历方法:
Map.prototype.keys():返回键名的遍历器。Map.prototype.values():返回键值的遍历器。Map.prototype.entries():返回所有成员的遍历器。Map.prototype.forEach():遍历 Map 的每个成员。
WeakMap
WeakMap 结构与 Map 结构类似,区别在于:
WeakMap的键名只能是对象,且是弱引用对象。 在没有其他引用和该键引用同一对象,这个对象将会被垃圾回收(相应的key则变成无效的),所以,WeakMap 是不能被遍历的。
Set
Set区别与数组的最大特点是成员无序且唯一,成员可以是任意类型。
Set 的属性、操作方法及遍历方法与Map类似
WeakSet
WeakSet 与 Set 的区别是,它的成员都是对象且都是被弱引用的,即垃圾回收机制不考虑 WeakSet 对该对象的应用。
应用
上面的学完之后,可以根据需要选择自己需要的数据结构。
下面介绍几种应用场景:
- 数组去重
Array.from(new Set([1, 2, 3, 1, 2])) // [1, 2, 3]
- 实现并集
(Union)、交集(Intersect)和差集
let set1 = new Set([1, 2, 3])
let set2 = new Set([4, 3, 2])
let intersect = new Set([...set1].filter(value => set2.has(value))) // Set {2, 3}
let union = new Set([...set1, ...set2]) // Set {1, 2, 3, 4}
let difference = new Set([...set1].filter(value => !set2.has(value))) // Set {1}
- WeakMap, vue3 源码 track函数收集依赖部分
// 是否应该收集依赖
let shouldTrack = true
// 当前激活的 effect
let activeEffect
// 原始数据对象 map
const targetMap = new WeakMap()
function track(target, type, key) {
if (!shouldTrack || activeEffect === undefined) {
return
}
let depsMap = targetMap.get(target)
if (!depsMap) {
// 每个 target 对应一个 depsMap
targetMap.set(target, (depsMap = new Map()))
}
let dep = depsMap.get(key)
if (!dep) {
// 每个 key 对应一个 dep 集合
depsMap.set(key, (dep = new Set()))
}
if (!dep.has(activeEffect)) {
// 收集当前激活的 effect 作为依赖
dep.add(activeEffect)
// 当前激活的 effect 收集 dep 集合作为依赖
activeEffect.deps.push(dep)
}
}