轉自:微信公衆號—— web前端教程
ES:給開發者提供了一個新特性:Proxy,就是代理的意思。也就是我們這一節要介紹的知識點。
以前,ATM還沒有那麼流行的時候(暴露年紀),我們去銀行存款或者取款的時候,需要在櫃檯前排隊,等櫃檯工作人員幫我們辦理業務,這也是一種代理,我們自己無法修改我們銀行賬戶上的數據,需要代理給櫃員,幫我們辦理存錢或者取現業務,而Proxy也是這樣的一種機制。
Proxy的實現
我們先來看看Proxy的實現:
//定義一個對象person
var person = {"name":"張三"};
//創建一個代理對象pro,代理person的讀寫操作
var pro = new Proxy(person,{
get:function(target,property){
return "李四"
}
});
pro.name;//李四
先定義一個對象,含有name屬性,值爲“張三”,創建一個代理對象pro,對象person的操作都交給代理對象pro,這不,看最後一句代碼,如果你要讀取person對象的name屬性,就要用pro.name,而不是person的name。我們看到的結果是:“李四“而不是person對象重點張三,因爲代理過程中,get方法實現了攔截的作用,不管你讀取什麼屬性,我都返回”李四“。
這就是代理Proxy的作用,將一個對象交給了Proxy代理,然後通過編寫處理函數,來攔截目標對象的操作。上面的案例,你可以理解爲,person對象的name屬性值不想被別人知道是“張三“,就設置了一個代理,讓別人讀取的時候,只獲取到”李四“。
set方法
而上面提到的“編寫處理函數“,get方法就是其中一個,除了get方法以外,還有一個很常用的是:set方法,它用於攔截對對象的寫操作。
我們來結合銀行的例子,來用get方法和set方法的實現,這段代碼有點長,但是註釋很詳細,注意看代碼註釋:
//定義一個對象,含有RMB和dollar屬性值
var bankAccount = {"RMB":1000,"dollar":0};
//創建一個Proxy代理實例
var banker = new Proxy(bankAccount,{
//編寫get處理程序
get:function(target, property){
//判斷餘額是否大於0
if(target[property] > 0){
//有餘額,就返回餘額值
return target[property];
}else{
//沒錢了
return "餘額不足";
}
},
//編寫set處理程序
set:function(target,property,value){
//存入的數額必須是一個數字類型
if(!Number.isInteger(value)){
return "請設置正確的數值";
}
//修改屬性的值
target[property] = value;
}
});
banker.RMB;
//結果:1000
banker.dollar;
//結果:餘額不足
//修改dollar屬性的值,值是字符串類型
banker.dollar = "五百";
banker.dollar;
//結果:餘額不足
//修改dollar屬性的值,值是數字類型
banker.dollar = 500;
banker.dollar;
//結果:500
幾乎每一句代碼都有註釋,這段代碼對應的故事情節是這樣的:老王有的銀行賬戶裏面有一些存款,其中人民幣1000元,美元0元。
var bankAccount = {"RMB":1000,"dollar":0};
有一天,他來到銀行櫃檯前,找到一個叫banker的工作人員,取款之前看看賬戶裏面還有多少錢,然後工作人員banker開始幫他操作(也就是代理)。
banker.RMB;
//結果:1000
banker.dollar;
//結果:餘額不足
banker告訴他:“您賬戶裏面有人民幣1000元,可以取款的,但美元餘額不足“。
接着,老王不打算取款了,打算存500美元。.
在填寫存款單據的時候,把500不小心寫成了“五百“,banker告誡老王:”這樣是寫不行的,一定要寫阿拉伯數字,這樣寫銀行無法幫您存款的“。結果存款失敗,賬戶裏面的美元還是0元。
banker.dollar = "五百";
banker.dollar;
//結果:餘額不足
沒關係,馬上改過來,把“五百“改成500。
banker.dollar = 500;
banker.dollar;
//結果:500
存款成功,賬戶裏面的美元已有500元。
故事的整個經過就是這樣,有了Proxy代理(銀行工作人員bank),幫助老王完成查看銀行存款和取款的操作(代理),避免了一些誤操作。
get方法攔截了讀取操作,set方法攔截了改寫操作。Proxy除了支持以上攔截程序,還支持一系列的攔截函數,我們選擇幾個常用的講解!
ownKeys( )方法
ownKeys攔截操作,攔截過濾Object.ownKeys()對對象的屬性遍歷。
//定義一個對象person,有三個屬性
let person = {"name":"老王","age":40,"height":1.8};
//創建一個代理對象
let proxy = new Proxy(person,{
//ownKeys過濾對對象的屬性遍歷
ownKeys:function(target){
return ["name","age"]
}
});
Object.keys(person);
//結果:["name", "age","height"]
Object.keys(proxy);
//結果:["name", "age"]
我們編寫的ownKeys方法程序,不管你有多少屬性,只返回兩個屬性name和age。我們看最後兩行代碼:Object.keys(person); 這裏我們不使用代理,直接用keys( )函數遍歷person對象,得到的person對象的原本屬性"name"、 "age"和"height"。而Object.keys(proxy) 這句代碼遍歷的是被代理的proxy對象,所以,得到的只是被過濾後的結果:[“name”,”age”]。
has( )方法
has( )攔截操作:攔截key in object的操作,結果會返回一個布爾值。
var person = {
"name":"張三",
"age":20
};
var proxy = new Proxy(person, {
has: function(target, prop) {
if(target[prop] === undefined){
return false;
}else{
return true;
}
}
});
"name" in proxy;//結果:true
"height" in proxy;//結果:false
has( )方法用於是判斷是否含有指定的鍵值對,有,就返回true。否則返回false。
對象含有name屬性,所以返回true,沒有height屬性,返回false。
apply( )方法
除了對象類型的變量可以被代理,函數也可以被代理。如果被代理的變量是一個函數,那麼還會支持一個攔截程序:apply調用。
//創建一個函數fn
let fn = function(){
alert('我是前端君');
};
//創建一個代理實例,代理函數fn
let proxy = new Proxy(fn,{
apply:function(){
alert('我是隔壁老王');
}
});
proxy();//結果:我是隔壁老王
最後一句代碼,proxy本身是一個代理實例對象,因爲它代理的是一個函數fn,所以可以直接用函數的形式調用proxy( );當它當作函數調用的時候,就會被apply攔截,執行alert('我是隔壁老王')。
Proxy.revocable( )取消代理
如果創建了代理之後又想取消代理的話,我們可以用Proxy.revocable( )函數來實現,它會返回一個對象,對象中含有一個proxy屬性,它就是Proxy的代理實例對象;還有一個revoke屬性,它是一個方法,用於取消代理。
我們用實例演示一下:
//定義一個對象
let person = {"name":"張三"};
//定義一個代理處理程序
let handle = {
get:function(target,prop){
return "李四";
}
};
//使用Proxy.revocable()進行代理
let object = Proxy.revocable(person,handle);
object.proxy.name;//結果:李四
//調用返回對象object的revoke方法,取消代理
object.revoke();
object.proxy.name;//報錯,代理被取消
這個案例大家要注意的是Proxy.revocable( )方法返回的結果,它是一個對象,在控制檯打印出來後的結果是:Object{ proxy:Object , revoke:function(){....} }。有一個proxy屬性,它就是Proxy代理實例,還有一個屬性revoke,它是一個方法,專用於取消代理。
我們使用object.proxy.name來讀取name的屬性,由於被代理攔截了,只能讀取到“李四”,接着我們調用revoke( )方法取消代理,然後再使用object.proxy.name的時候就會報錯了,代理已經不存在了。
以上就是Proxy代理的介紹,關於其他的攔截操作就不一一介紹,包括:
-
defineProperty( )
-
deleteProperty( )
-
enumerate( )
-
getOwnPropertyDescriptor( )
-
getPrototypeOf( )
-
isExtensible( )
-
preventExtensions( )
-
setPrototypeOf( )
本節小結
總結:ES6帶了的Proxy代理機制,它提供了一些攔截操作:set、get、apply、has、ownKeys等,我們可以根據需求編寫攔截程序,達到我們想要的效果;此外,還可以利用Proxy.revocable( )實現取消代理。