observify-obj
by jin5354
Babel + JSX
// more detail go to
// https://github.com/jin5354/leaf-observable
// Dep 类,保存数据源的所有订阅,并在接收到数据源的变动通知后,触发所有订阅
class Dep {
constructor() {
this.subs = []
}
addSub(sub) { // 添加订阅
this.subs.push(sub)
}
notify(newValue, oldValue) {
this.subs.forEach(sub => {
sub.update(newValue, oldValue) // 触发订阅
})
}
}
const globalDep = new Dep()
// Watcher 类,每个 Watcher 为一个订阅源
class Watcher {
constructor(callback) {
this.callback = callback
}
update(newValue, oldValue) {
this.callback(newValue, oldValue) // 被触发后执行回调
}
}
// 使一个对象响应化
function observify(value) {
if(!isObject(value)) {
return
}
Object.keys(value).forEach((key) => {
defineReactive(value, key, value[key]) // 遍历每个键使其响应化
})
}
function isObject(value) {
return typeof value === 'object' && value !== null
}
//为对象的一个键应用 Object.defineProperty
function defineReactive(obj, key, value) {
observify(value) // 递归
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
return value
},
set: (newValue) => {
if(newValue === value) {
return
}else {
let oldValue = value
value = newValue
observify(newValue)
globalDep.notify(newValue, oldValue) // 变动时通知 Dep
}
}
})
}
let o = {
a: 1,
c: {
d: 1
}
}
//『使数据响应化』和『添加回调函数』两个操作已被解耦,解耦后可以方便的多次添加订阅
// 使数据响应化
observify(o)
// 添加订阅
globalDep.addSub(new Watcher((newValue, oldValue) => {
console.log('发生改变!新值:', newValue, ",旧值:", oldValue)
}))
o.a = 2 // 输出:发生改变!新值: 2 ,旧值: 1
o.c.d = 4 // 输出:发生改变!新值: 4 ,旧值: 1
// 可以再添加一个订阅
globalDep.addSub(new Watcher((newValue, oldValue) => {
console.log('新订阅')
}))
o.a = 3 // 输出:发生改变!新值: 3 ,旧值: 2
// 输出:新订阅