1. 程式人生 > >深入理解ES6之——代理和反射(proxy)

深入理解ES6之——代理和反射(proxy)

通過呼叫new proxy()你可以建立一個代理來替代另一個物件(被稱為目標),這個代理對目標物件進行了虛擬,因此該代理與該目標物件表面上可以被當做同一個物件來對待。

建立一個簡單的代理

當你使用Proxy構造器來建立一個代理時,需要傳遞兩個引數:目標物件以及一個處理器,後者是定義了一個或多個陷阱函式的物件。如果未提供陷阱函式,代理會對所有操作採取預設行為。

使用set陷阱函式驗證屬性值

let target = {};
var proxy = new Proxy(target, {
    set(trapTarget, key, value, receiver) {
        if (!trapTarget.hasOwnProperty(key)) {
            if (isNaN(value)) {
                throw new Error('Proxy must be a number');
            }
        }

        return Reflect.set(trapTarget, key, value, receiver);
    }
})

proxy.count = 1;

console.log(proxy.count);
console.log(target.count);

try {
    proxy.anthorName = 'cc';
} catch (err) {
    console.log(err.message);
}

使用上述方法可以對新增給物件的屬性值進行驗證,如果值為非數字,就會丟擲錯誤。

使用get陷阱函式進行物件外形驗證

在js中,如果讀取一個物件中不存在的屬性時,會顯示undefined,這對於排查問題很不利。使用代理進行物件外形驗證就可以幫你從這個錯誤中拯救出來。

let proxy = new Proxy({}, {
    get(trapTarget, key, receiver) {
        if (!(key in receiver)) {
            throw new Error(`property ${key} not exist`);
        }
        return Reflect.get(trapTarget, key, receiver);
    }
})

proxy.name = 'cc';
try {
    console.log(proxy.age);
} catch (error) {
    console.log(error.message);
}

//輸出結果
property age not exist

上述程式碼對列印的物件屬性進行驗證,如果不存在則丟擲一個錯誤。今日頭條的一個面試題

使用has陷阱函式隱藏屬性

in運算子用於判斷指定物件中是否存在某個屬性,如果物件的屬性名與指定的字串或符號值相匹配,那麼in運算子應當返回true,無論該屬性是物件自身的屬性還是原型的屬性。代理允許你使用has陷阱函式來解決這個問題

has陷阱函式會在使用in運算子的情況下被呼叫,並且會被傳入兩個引數:

  1. trapTarget:需要讀取屬性的物件(即代理的目標物件)
  2. key:需要檢查的屬性的鍵(字串型別或符號型別)Reflect.has()方法接收與之相同的引數並向in運算子返回預設相應結果
let target = {
    name: 'cc',
    age: 26,
    sex: 'man'
}

let proxy = new Proxy(target, {
    has(trapTarget, key) {
        if (trapTarget.hasOwnProperty(key)) {
            return Reflect.has(trapTarget, key);
        } else {
            return false;
        }
    }
})

console.log('toString' in proxy);
console.log('name' in proxy);
console.log('age' in proxy);

使用deleteProperty陷阱函式避免屬性被刪除

delete運算子能從指定物件上刪除一個屬性,在刪除成功時返回true,否則返回false

deleteProperty陷阱函式會在使用delete運算子去刪除物件屬性時被呼叫,並且會被傳入兩個引數:

  1. trapTarget:需要刪除屬性的物件
  2. key:需要刪除的屬性的鍵

Reflect.deleteProperty()方法也接受兩個引數,並提供了deleteProperty陷阱函式的預設實現。

let target = {
    name: 'target',
    value: 42
}

let proxy = new Proxy(target, {
    deleteProperty(trapTarg, ke) {
        if (ke === 'value') {
            return false;
        } else {
            return Reflect.deleteProperty(trapTarg, ke);
        }
    }
})

let result = delete proxy.value;
let result1 = delete proxy.name;
console.log(result);
console.log(result1);