模式构成
- 观察者
观察者又可称为信息改变后被通知的对象,例如:商品价格改变,通知那些关注此商品的顾客,此时顾客即为观察者。又例如,vue中响应式数据改变后,通知那些依赖做出响应(如:vue中插值表达式{{name}},name改变页面显示的数据也更新,也就是重新更新dom,此时更新dom操作即为观察者),依赖即为观察者 - 被观察对象
被观察者即为观察者观察的主体,如上例中的商品为被观察者,上例中的vue的响应式数据也为被观察者。
实现观察者模式
1.商品价格改变例子
class Subject {
constructor() {
this.observers = []
}
notify(val) {
this.observers.forEach(observer => observer.update(val))
}
add(observer) {
this.observers.push(observer)
}
remove(observer) {
this.observers.splice(this.observers.findIndex(observer), 1)
}
}
class Watcher {
constructor() {}
update(val) {
console.log(`数据改变了${val}`)
}
}
class Goods {
_price = ''
priceWatcher = null
constructor() {
this.priceWatcher = new Subject()
}
set price(val) {
this._price = val
this.priceWatcher.notify(val)
}
get price() {
return this._price
}
addPriceWatcher(watcher) {
this.priceWatcher.add(watcher)
}
}
function focusPrice(good) {
const watcher = new Watcher()
good.addPriceWatcher(watcher)
}
2.vue中数据改变插值表达式例子
function defineReactive(obj, key, val) {
const subject = new Subject()
Object.defineProperty(obj, key, {
get() {
Subject.target && subject.add(Subject.target)
return val
},
set(newVal) {
console.log(newVal , val)
if(newVal !== val) {
val = newVal
subject.notify()
}
}
})
}
// 主体
class Subject {
constructor() {
this.observers = []
}
add(dep) {
this.observers.push(dep)
}
notify() {
this.observers.forEach(dep => {
dep.update()
})
}
}
class Watcher {
constructor(vm, key, updateFn) {
this.vm = vm
this.key = key
this.updateFn = updateFn
Subject.target = this
this.vm[this.key] // 触发get 收集依赖
Subject.target = null
}
update() {
console.log(this.vm, 'update')
this.updateFn.call(this.vm, this.key)
}
}
// 渲染
const obj = {time: Date.now()}
function compileText(node) {
const reg = /\{\{(.+)\}\}/
const textContent = node.textContent
if(reg.test(textContent)) {
const key = RegExp.$1
renderText(node, obj, key)
new Watcher(obj, key, function(exp) {
renderText(node, obj, exp)
})
}
}
function renderText(node, vm, exp) {
console.log(vm, exp)
node.textContent = vm[exp]
}
function compile() {
const node = document.querySelector('#demo')
compileText(node)
}
Object.keys(obj).forEach((key) => {
defineReactive(obj, key, obj[key])
})
compile()
setInterval(() => {
obj.time = Date.now()
}, 1000)