數據劫持與數據代理

數據劫持

數據劫持即便用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'
    }
}

當遇到變異方法時舊版本的vue經過重寫方法來進行數據劫持

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代理對象(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)
  }
}
相關文章
相關標籤/搜索