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
        // 输出:新订阅