day97-Proxy-实现观察者模式

前言

Vue3.0之后用Proxy代替Object.defineProperty。来尝个鲜,看看怎样实现一个简单的观察者模式。


代码

class Dep { // 添加订阅者,通知变化(依赖收集类Dep)
constructor() {
this.subs = new Set()
}
addSub(sub) { // 添加订阅者
this.subs.add(sub)
}
notify(key) { // 通知订阅者更新
// 修改data中数据的时候会触发Watcher的notify,从而回调渲染函数
this.subs.forEach(sub => {
sub.update()
})
}
}
class Watcher { // 观察者,订阅数据变化,绑定更新函数,添加订阅者,更新视图
constructor(obj, key, cb) {
this.obj = obj,
this.key = key,
this.cb = cb // 回调
this.value = this.get()
}
get() { // 将自身添加到dep中
// 观察者Watcher实例赋值给全局的Dep.target
// 触发render操作只有被Dep.target标记过的才会进行依赖收集
Dep.target = this // 将当前订阅者指向自己
let value = this.obj[this.key] // 触发getter,添加自己到属性订阅器中
Dep.target = null // 添加完毕,重置
return value
}
update () { // 观察是否有新的值
let newVal = this.obj[this.key]
if (this.value !== newVal) {
this.cb(newVal)
this.value = newVal
}
}
}
function Observer (obj) { // 劫持监听所有属性
Object.keys(obj).forEach(key => {
if (typeof obj[key] === 'object') {
obj[key] = Observer(obj[key])
}
})
let dep = new Dep()
let handler = {
get: function (target, key, receiver) {
// 依赖收集的时候回addSub到sub中
Dep.target && dep.addSub(Dep.target)
return Reflect.get(target, key, receiver)
},
set: function (target, key, value, receiver) {
let result = Reflect.set(target, key, value, receiver)
dep.notify() // 发布
return result
}
}
return new Proxy(obj, handler)
}
// 例子
let data = {
name: 'lmislm'
}
function who(data) {
console.log('my name is', data)
}
function what(data) {
console.log('just do', data)
}
data = Observer(data)
new Watcher(data, 'name', who)
// 新值旧值一样需要判断
data.name = 'lmislm2'
new Watcher(data, 'what', what)
data.what = 'IT'
// my name is lmislm2
// just do IT
文章作者: lmislm
文章链接: http://lmislm.com/2019/04/19/2019-04-19/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 LMISLMのBlog