數據劫持即便用Object.defineProperty()實現了vue的雙向綁定。先來看看它是如何實現的vue
let obj = {}, txt = '' Object.defineProperty(obj,'txt',{ set(val) { console.log('set ....') txt = val || ''; }, get() { //獲取obj.txt時會觸發get的回調。 console.log('get ....') return txt } })
Object.defineProperty的缺點
一、沒法監聽到數組的變化
舉個例子數組
//當被監聽的屬性是數組時 let arr = [1,2,3] let obj = {} Object.defineProperty(obj,'arr',{ set(val) { console.log('set',val) arr = val }, get() { console.log('get') return arr } }) obj.arr.push(4) // get 其實是改變arr的值可是卻沒有執行set而是執行了get obj.arr = [1,2,3,4] // 執行了set
當被監聽的屬性是數組,這幾個方法push、pop、shift、unshift、splice、sort、reverse不會觸發set。vue將這幾個修改原始的數組的方法稱爲變異方法app
二、必須遍歷對象的每個屬性測試
Object.keys(obj).forEach(key=>{ Object.defineProperty(obj,key,{ //.... }) })
三、必須深層遍歷嵌套對象ui
let person = { name:{ firstName:'chan', lastName:'louis' } }
const aryMethods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse']; const arrayAugmentations = []; aryMethods.forEach((method)=> { // 這裏是原生 Array 的原型方法 let original = Array.prototype[method]; // 將 push, pop 等封裝好的方法定義在對象 arrayAugmentations 的屬性上 // 注意:是實例屬性而非原型屬性 arrayAugmentations[method] = function () { console.log('has change'); // 調用對應的原生方法並返回結果 return original.apply(this, arguments); }; }); let list = ['a', 'b', 'c']; // 將咱們要監聽的數組的原型指針指向上面定義的空數組對象 // 這樣就能在調用 push, pop 這些方法時走進咱們剛定義的方法,多了一句 console.log list.__proto__ = arrayAugmentations; list.push('d'); // 我被改變啦! // 這個 list2 是個普通的數組,因此調用 push 不會走到咱們的方法裏面。 let list2 = ['a', 'b', 'c']; list2.push('d'); // 不輸出內容
proxy即代理的意思。我的理解,創建一個proxy代理對象(Proxy的實例),接受你要監聽的對象和監聽它的handle兩個參數。當你要監聽的對象發生任何改變,都會被proxy代理攔截來知足需求。this
var arr = [1,2,3] var handle = { //target目標對象 key屬性名 receiver實際接受的對象 get(target,key,receiver) { console.log(`get ${key}`) // Reflect至關於映射到目標對象上 return Reflect.get(target,key,receiver) }, set(target,key,value,receiver) { console.log(`set ${key}`) return Reflect.set(target,key,value,receiver) } } //arr要攔截的對象,handle定義攔截行爲 var proxy = new Proxy(arr,handle) proxy.push(4) //能夠翻到控制檯測試一下會打印出什麼
一、使用proxy能夠解決defineProperty不能監聽數組的問題,避免重寫數組方法;
二、不須要再遍歷key。
三、Proxy handle的攔截處理器除了get、set外還支持多種攔截方式,具體請查閱官方文檔(https://developer.mozilla.org...)
四、嵌套查詢。實際上proxy get()也是不支持嵌套查詢的。解決方法:prototype
let handler = { get (target, key, receiver) { // 遞歸建立並返回 if (typeof target[key] === 'object' && target[key] !== null) { return new Proxy(target[key], handler) } return Reflect.get(target, key, receiver) } }