# 簡略版+本身的註釋vue
// 判斷一個變量是不是對象 function isObject(obj) { return obj.constructor === Object } class Observer { constructor(value) { this.value = value; if (!arr.isArray(value)) { this.walk(value); } } walk(obj) { const keys = Object.keys(obj); // 循環將obj中的每個屬性轉換成getter/setter進行變化追蹤 for (let i = 0; i < keys.length; i++) { defineReactive(obj, keys[i], obj[keys[i]]); } } } function defineReactive(data, key, val) { if (isObject(val)) { new Observer(val); // 進行遞歸調用 } let dep = new Dep(); Object.defineProperty(data, key, { configurable: true, enumerable: true, get: function () { dep.depend(); return val; }, set: function (newVal) { if (val === newVal) return // 若是賦值的新值也是一個對象 也須要進行偵測 if (isObject(newVal)) { new Observer(val); // 進行遞歸調用 } val = newVal; dep.notify(); // 通知全部的訂閱者,數據要被修改了,作出相應的行爲(也就是執行對應的回調函數) } }) } class Dep { constructor() { this.subs = [] // 這個裏面存放的是Watch實例對象 } addSub(sub) { this.subs.push(sub); // 在這個地方收集訂閱者 } removeSub(sub) { remove(this.subs, sub); } depend() { if (window.target) { this.addSub(window.target); // 在這個地方觸發depend方法,進行收集訂閱者 } } notify() { const subs = this.subs.slice(); for (let i = 0; i < subs.length; i++) { subs[i].update(); // 在這個地方執行回調函數 } } } function remove(arr, item) { if (arr.length) { const index = arr[item]; if (index > -1) { return arr.splice(index, 1); } } } class Watcher { constructor(vm, expOrFn, cb) { this.vm = vm; this.getter = parsePath(expOrFn); this.cb = cb; this.value = this.get(); // 獲取expOrFn中的值 在這個內部同時會觸發getter,從而在dep中添加本身 } get() { window.target = this; // 將watch實例對象賦值給全局的target變量上 let value = this.getter.call(this.vm, this.vm); // 該代碼的做用很關鍵 // 若是expOrFn直接是一個表達式不是一個函數 eg: 'name', 'age' 假設只有一個屬性 不是這種的 'name.a.b' // 咱們就能夠直接下面這樣寫,只是爲了測試理解,vue.js源碼處理更加全面 // let value = this.vm[this.expOrFn] // vm就是要監聽的數據,固然expOrFn要在constructor中掛在到this身上 window.target = undefined; return value; } update() { const oldValue = this.value; this.value = this.get(); this.cb.call(this.vm, this.value, oldValue); } } // 該方法的做用是將 'name.a.b'這種表達式的值取出來 // 也就是經過該方法返回的是 obj['name']['a']['b]的值 function parsePath(path) { const bailRE = /[^\w.$]/; const segments = path.split('.') // 閉包 return function (obj) { for (let i = 0; i < segments.length; i++) { if (!obj) return obj = obj[segments[i]] // 取出屬性的值 因此這個地方會執行getter } return obj; // 返回屬性的值 } } <script> let btns = document.getElementsByTagName('button'); // 定義數據 let person = {}; // 定義響應式數據的屬性 defineReactive(person, 'name', '喬峯') // 監聽數據 let w = new Watcher(person, 'name', function (newVal, oldVal) { console.log('數據發生變化了') console.log(newVal, oldVal); }) // 1.取值操做 person.name // 2.改變數據 btns[0].onclick = function () { person.name = '小龍女'; }
# 總結c#
1.在實例化一個Watcher對象時,其get方法中會觸發defineReactive中的getter訪問器,在其getter訪問器中會執行dep.depend()方法,dep.depend()方法會調用this.subs.push(sub)方法,從而收集依賴,也就是在subs 中存放的是當前的Watcher實例對象閉包
2.Watch實例對象自身必須有一個update方法ide
3.當數據發生變化時,會觸發defineReactive中的setter訪問器,在其setter訪問器中會調用subs[i].update()方法,其中的每個subs[i]就是watch實例對象,從而執行了update方法,在update方法中執行了this.cb.call(this.vm, this.value, oldValue)回調函數函數
3.Observer 類是用來將傳遞進來的數據遍歷轉換成響應式數據,也就是轉換成getter/setter的形式進行偵測測試
4.在使用的時候,咱們首先須要調用defineReactive方法,來建立一個響應式數據,而後再調用Watcher方法來監聽這個響應式數據的變化,而後就能作到數據驅動,或者叫響應式數據this
______若有不對,請指正_______spa