本章先模拟vue3实现一个功能最简单的数据响应式系统.
副作用函数
当函数的执行会直接或间接影响其他函数的执行, 我们就称这个函数为副作用函数. 副作用函数很容易产生, 如函数中修改了全局变量. 如下面的effect函数.
1 2 3 4 5
| let val = 1 function effect(){ val = 2 }
|
实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| const bucket = new WeakMap()
let activeEffect
const data = { text: 'hello world'}
const obj = new Proxy(data, { get(target, key) { if (!activeEffect) return target[key] let depsMap = bucket.get(target) if (!depsMap) { bucket.set(target, (depsMap = new Map())) } let deps = depsMap.get(key) if (!deps) { depsMap.set(key, (deps = new Set())) } deps.add(activeEffect)
return target[key] }, set(target, key, newVal) { target[key] = newVal const depsMap = bucket.get(target) if(!depsMap) return const effects = depsMap.get(key) effects && effects.forEach((fn) => fn()); return true } })
function effect(fn) { activeEffect = fn fn() }
effect(() => { document.body.innerHTML = obj.text; })
setTimeout(() => { obj.text = '1秒后改变数据' obj.text2 = '123' }, 1000);
|
WeakMap 还是 Map ?
为了细化副作用函数的绑定, 我们用到了Map来存储对象的副作用函数. 格式为{target1: {key1: Set1, key2: Set2}, target2: {key1: Set1}}, 如下图所示:

其中Set为集合, 存放副作用函数.
Map与WeakMap的区别
1 2 3 4 5 6 7 8 9
| const map = new Map() const weakmap = new WeakMap()
; (function () { const foo = { foo: 1 } const bar = {bar: 2} map.set(foo, 1) weakmap.set(bar, 2) })()
|
foo和bar分别作为Map和WeakMap的key. 当函数表达式执行结束, 对于对象foo来说, 它仍作为map的key被引用着, 因此垃圾回收器不会把它从内存中移除.
然而对于对象bar来说, 由于WeakMap的key是弱引用, 它不影响垃圾回收器的工作.所以一旦表达式执行完毕, 垃圾回收器就会把对象bar从内存中移除, 并且也无法获取weakmap的key值, 也无法通过weakmap取的对象bar.
总结: WeakMap对key是弱引用, 不影响垃圾回收器的工作. 一旦key被垃圾回收器回收, 那么对应的键和值就访问不到了. 所以WeakMap经常用于存储那些只有当可以所引用的对象存在时(没有被回收)才有价值的信息.
为什么是WeakMap
知道了上面Map与WeakMap的区别, 就不难理解. 当target被垃圾回收器回收后, 我们自然也不再需要继续执行对应target下的副作用函数. 所以这边用WeakMap来作为存储集合.