ES6之Proxy

一、定義

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

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

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

三、作用

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

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

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

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 // 拋出一個(gè)錯(cuò)誤

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

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');
            }
        }

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

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

person.age = 100;
console.log(person.age)     // 100
person.age = 'young'     // 報(bào)錯(cuò)
person.age = 300     // 報(bào)錯(cuò)
五、實(shí)例

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)建一個(gè)test對(duì)象,里面有name屬性,然后使用Proxy將其包裝起來,再返回給test,此時(shí)的test已經(jīng)成為一個(gè)Proxy實(shí)例,我們對(duì)其的操作都會(huì)被Proxy攔截。handler中定義了get函數(shù),當(dāng)獲取test屬性時(shí)會(huì)觸發(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對(duì)象的name屬性設(shè)置為value。返回值為 boolean ,true 表示修改成功,false 表示失敗。當(dāng) target 為不存在的對(duì)象時(shí),會(huì)報(bào)錯(cuò)
    }
});
console.log(`我叫${julian.name}  今年${julian.age}了`);     // 獲取name和age的屬性值
julian.age = "not a number";     // 報(bào)錯(cuò)
//julian.age = 20;
//console.log(`我叫${julian.name}  我今年${julian.age}了`);      // 正常輸出并修改了age的值

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

六、ES3、ES5、ES6實(shí)現(xiàn)同一個(gè)例子的對(duì)比

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

2、ES5實(shí)現(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實(shí)現(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實(shí)現(xiàn)結(jié)果.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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