ES6之Proxy

一、定義

Proxy用于修改某些操作的默認行為,可以理解成在目標對象之前架設(shè)一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。換句話說就是Proxy對象就是允許對JS中的一切合法對象的基本操作進行自定義,然后用自定義的操作去覆蓋其對象的基本操作,說白了就是重寫其所屬類或構(gòu)造函數(shù)中的基本操作

二、語法
let proxy = new Proxy(target, handler)

target:目標對象(待操作對象),需要使用Proxy包裝的目標對象(可以是任何類型的對象,包括原生數(shù)組、函數(shù),甚至另一個代理)
handler:自定義操作方法的一個對象,,最多可包含13個操作方法,也可以是空對象
proxy:一個被代理后的新對象,它擁有target的一切屬性和方法,只不過其行為和結(jié)果是在handler中自定義的。在一定程度上可以看成是對target的引用

三、作用

1、在目標對象之前架設(shè)一層攔截,外界對該對象的訪問都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫
2、ES6原生提供Proxy構(gòu)造函數(shù),用來生成Proxy實例
3、通過handler對象中的攔截方法攔截目標對象target的某些操作(如讀取、賦值、函數(shù)調(diào)用、new等),然后過濾或者改寫這些操作的默認行為
4、生成的實例對象是針對target對象的攔截器,也可以叫做代理器
5、如果handler是個空對象({}),那么操作攔截器相當于直接操作目標對象target

四、Proxy攔截器的13種方法(只介紹其中的兩種)

1、get方法
get(target, propKey, receiver):攔截某個屬性的讀取操作,接收三個參數(shù),依次為目標對象、屬性名、和Proxy實例本身,最后一個參數(shù)是可選的,一般情況下指向的是proxy對象,但是如果proxy作為其他對象的原型時,則指向讀取該屬性的對象

var person = {
    name: "張三"
};

var proxy = new Proxy(person, {
    get: function(target, property) {
        if (property in target) {
            return target[property];
        }else{
            throw new ReferenceError("Property \"" + property + "\" does not exist.");
        }
    }
});

proxy.name // "張三"
proxy.age // 拋出一個錯誤

2、set方法
set(target, propKey, value, receiver):攔截某個屬性的賦值操作,接收4個參數(shù),依次為目標對象、屬性名、屬性值和Proxy實例本身,最后一個參數(shù)是可選的。返回一個布爾值

let validator = {
    set: function(obj, prop, value) {
        if (prop === 'age') {
            if (!Number.isInteger(value)) {
                throw new TypeError('The age is not an integer');
            }
            if (value > 200) {
                throw new RangeError('The age seems invalid');
            }
        }

        // 對于age以外的屬性,直接保存
        obj[prop] = value;  
    }
};

let person = new Proxy({}, validator);

person.age = 100;
console.log(person.age)     // 100
person.age = 'young'     // 報錯
person.age = 300     // 報錯
五、實例

1、get方法應(yīng)用

let test = {
    name: "Julian小龍蝦"
};
test = new Proxy(test, {
    get(target, key) {
        console.log('獲取getter屬性');
        return target[key];
    }
});
console.log(test.name);

解析:首先創(chuàng)建一個test對象,里面有name屬性,然后使用Proxy將其包裝起來,再返回給test,此時的test已經(jīng)成為一個Proxy實例,我們對其的操作都會被Proxy攔截。handler中定義了get函數(shù),當獲取test屬性時會觸發(fā)此函數(shù)

2、set方法應(yīng)用

let julian = {
    name: "Julian",
    age: 18
};
julian = new Proxy(julian, {
    get(target, key) {
        let result = target[key];
        //如果是獲取 年齡 屬性,則添加 歲字
        if (key === "age"){
            result += "歲";
        }
        return result;
    },
    set(target, key, value) {
        if (key === "age" && typeof value !== "number") {
            throw Error("age字段必須為Number類型");
        }
        return Reflect.set(target, key, value);     //將target對象的name屬性設(shè)置為value。返回值為 boolean ,true 表示修改成功,false 表示失敗。當 target 為不存在的對象時,會報錯
    }
});
console.log(`我叫${julian.name}  今年${julian.age}了`);     // 獲取name和age的屬性值
julian.age = "not a number";     // 報錯
//julian.age = 20;
//console.log(`我叫${julian.name}  我今年${julian.age}了`);      // 正常輸出并修改了age的值

解析:定義了julian對象,其中有age和name兩個字段,我們在Proxy的get攔截函數(shù)中添加了一個判斷,如果是取age屬性的值,則在后面添加。在set攔截函數(shù)中判斷如果是更改age屬性時,類型不是number則拋出錯誤。

六、ES3、ES5、ES6實現(xiàn)同一個例子的對比

要求:有一組數(shù)據(jù),有name、age、sex三個屬性,其中name和age是可讀可寫屬性,但是sex是只讀屬性,用ES3、ES5、ES6分別實現(xiàn)
實現(xiàn)步驟:首先頂一個構(gòu)造函數(shù),內(nèi)部定義一個data數(shù)據(jù),包含name、age、sex三個屬性,一個get方法,一個set方法。get方法用來讀數(shù)據(jù),set方法用來寫數(shù)據(jù),寫數(shù)據(jù)的時候進行判斷,如果設(shè)置的是sex屬性就給出錯誤提示
1、ES3實現(xiàn)

var Person = function(){
    var data = {
        name:'Julian小龍蝦',
        age:18,
        sex:'男'
    }
    this.get = function(key){
        console.log(key)
        return data[key]
    }
    this.set = function(key,value){
        if(key!=='sex'){
            return data[key] = value
        }else{
            throw '該屬性為只讀屬性'
        }
    }
}
var person = new Person;
var name = person.get('name')
person.set('sex','女')
console.log(person.get('sex'))
ES3實現(xiàn)結(jié)果.png

2、ES5實現(xiàn):通過defineProperty方法設(shè)置sex戶型為不可寫屬性

var person = {
    name:'Julian小龍蝦',
    age:30
}
Object.defineProperty(person,'sex',{
    writable:false,
    value:'男'
})
person.sex = '女'
console.log(person.sex)     // 男

3、ES6實現(xiàn)

var person = {
    name:'Julian小龍蝦',
    age:18,
    sex:'男'
}
var p1 = new Proxy(person,{
    get(target,key){
        console.log(target)
        console.log(key)
        return target[key]
    },
    set(target,key,value){
        if(key=='sex'){
            throw '不允許修改sex'
        }else{
            target[key] = value
        }
    }
})
p1.name
p1.sex = '女'
ES6實現(xiàn)結(jié)果.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容