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