vue中利用Object.defineProperty定义getter/setter实现了数据的响应式,里面重要的一点就是依赖收集,它是计算属性和被依赖属性之间的桥梁,有了依赖收集(Dep),当被依赖对象A的getter函数执行时,全部依赖它的东东就会被收集到A的依赖库中,当A的setter执行时,依赖库中的东东就会被一个一个执行,通知依赖对象B。而这些被封装的依赖是在B的getter执行的时候注入到Dep的静态属性target中的javascript
图很丑,包涵,下面的数字标号如①表示代码执行的顺序vue
/* * 定义一个“依赖收集器” * */ class Dep { constructor () { this.deps = [] } /* 收集依赖 */ depend () { if (Dep.target && this.deps.indexOf(Dep.target) === -1) { this.deps.push(Dep.target) } } /* 执行依赖 */ notify () { this.deps.forEach((dep) => { dep() }) } } /* target是被观察者的回调执行结果和计算属性被更新后调用的函数的封装 */ Dep.target = null /* 把一个对象的每一项都转化成可观测对象 */ class Observable { constructor (obj) { return this.walk(obj) } walk (obj) { const keys = Object.keys(obj) keys.forEach((key) => { this.defineReactive(obj, key, obj[key]) }) return obj } /* 使一个对象转化成可观测对象 */ defineReactive (obj, key, val) { const dep = new Dep() Object.defineProperty(obj, key, { get () { dep.depend() return val }, set (newVal) { val = newVal dep.notify() } }) } } /* * 观察者 * obj: 被观察对象 * key: 被观察者key * cb: 回调函数,返回“计算属性”的值 * onComputedUpdate: 当计算属性的值被更新时调用*/ class Watcher { constructor (obj, key, cb, onComputedUpdate) { this.obj = obj this.key = key this.cb = cb this.onComputedUpdate = onComputedUpdate return this.defineComputed() } defineComputed () { const self = this const onDepUpdated = () => { const val = self.cb() this.onComputedUpdate(val) } Object.defineProperty(self.obj, self.key, { get () { Dep.target = onDepUpdated const val = self.cb() Dep.target = null return val }, set () { console.error('计算属性没法被赋值!') } }) } }
运行一下:java
const hero = new Observable({ health: 3000, IQ: 150 }) new Watcher(hero, 'type', () => { return hero.health > 4000 ? '坦克' : '脆皮' }, (val) => { console.log(`个人类型是:${val}`) }) console.log(`英雄初始类型:${hero.type}`) hero.health = 5000 // -> 英雄初始类型:脆皮 // -> 个人类型是:坦克
参考资料:函数
https://zhuanlan.zhihu.com/p/29318017this