總目錄
從C#到TypeScript - Proxy
我們知道在C#中要實現代理功能需要自己來實現代理類,並且每個類需要不同的代理類,使用起來不方便,雖然借助一些AOP框架可以一定程度實現攔截,但畢竟框架級別的還是太重了。
現在ES6倒是出了個解決方案,Proxy是ES6推出的用於攔截操作的一層代理實現,TypeScript當然也同樣支持,下面來看下Proxy是怎么用的。
Proxy使用
Proxy本身是一個類,可以通過new來實例化一個代理。
let p = new Proxy(target, handle)
Proxy有兩個參數:
target指所要代理的對象。
handle也是一個對象,對象里包含對target操作的攔截。
看個例子:
let obj = { name: 'brook' };
let p = new Proxy(obj, {
get(target, property){
return 'cnblogs';
}
});
console.info(obj.name); // brook
console.info(p.name); // cnblogs
可以看到,p做為obj的代理,在handle里加了對目標對象的屬性get操作進行攔截,所以第一次直接輸出obj的name是'brook',用代理p輸出就變成'cnblogs'了。
因為handle里對獲取屬性操作進行了重新定義。
get函數同樣有兩個參數,target仍然是操作對象,另一個property則是要訪問的屬性的名字。
Proxy可攔截的操作
-
get(target, propKey, receiver)
-
set(target, propKey, value, receiver)
-
apply(target, object, args)
-
defineProperty(target, propKey, propDesc)
-
deleteProperty(target, propKey)
-
has(target, propKey)
-
ownKeys(target)
-
construct(target, args)
-
getPrototypeOf(target)
-
setPrototypeOf(target, proto)
-
getOwnPropertyDescriptor(target, propKey)
-
isExtensible(target)
-
preventExtensions(target)
看過上一篇Reflect的有沒有很熟,沒錯,Reflect里的操作Proxy里都同樣有一份,這樣在做Proxy的時候,如果要回到原始的結果,直接調用Reflect對應的操作就好。
接下來挑幾個重要的看看。
get
get(target, propKey, receiver)
上面提到過get,不過沒說第三個參數,其實receiver指的就是new出來的Proxy對象。
let obj = { name: 'brook' };
let p = new Proxy(obj, {
get(target, property, receiver){
console.info(receiver === p); // true
return 'cnblogs'
}
});
console.info(p.name);
再來個例子來看看get能做到什么程度,我們知道數組的索引不能為負數,現在我們通過Proxy來讓數組來支持它:
let arr = ["b", "r", "o", "o", "k"];
let p = new Proxy(arr, {
get(target, property){
let index = Math.abs(Number(property)); // 取負數的絕對值
return arr[index];
}
});
console.info(arr[2]); // 輸出o
console.info(p[-2]); //同樣輸出o
set
set(target, propKey, value, receiver)
set用來攔截屬性的賦值操作,比如number類型的數組,可以讓它接受任何類型的值,當不是number的時候就給值0,當然這只是個不符合實際使用的功能演示:
let arr = new Array<number>();
let p = new Proxy(arr, {
set(target, property, value, receiver){
if(typeof value != 'number'){ // 不是number就設為0
value = 0;
}
return Reflect.set(target, property, value);
}
});
p[0] = 11;
p[1] = "brook";
console.info(arr[0]); // 11
console.info(arr[1]); // 0
現在前端MVVM很火,而用set就可以輕松做到設置屬性值的同時更新Dom對象,實現綁定的效果。
apply
apply(target, object, args)
這可以攔截函數的調用,第一個和第三個參數的意思很明確,分別指函數和函數的參數。
第二個參數是指上下文的this,this的不同會可能導致函數里變量值的不同。
class Test1{
text: string = 'test1';
func(){
console.info('call test1 func')
console.info(`I am brook in ${this.text}`);
}
}
class Test2{
text: string = 'test2';
func(){
console.info('call test2 func')
console.info(`I am brook in ${this.text}`);
}
}
let t1 = new Test1();
let t2 = new Test2();
let p = new Proxy(t1.func, {
apply(target, thisArg, args){
Reflect.apply(target, t2, args);
}
});
p();
上面代碼輸出信息如下:
call test1 func
I am brook in test2
也就是實際調用的還是Test1的func,所以第一條輸出為call test1 func,雖然Proxy代理的是Test1的func,但實際執行時傳的this是t2,所以函數里的this指向了Test2,取的也就是test2中要實現代理功能需要自己來實現代理類,並且每個類需要不同的代理類,使用起來不方便,雖然借助一些AOP框架可以一定程度實現攔截,但畢竟框架級別的還是太重了。
上面介紹了幾個常用的,其他的意思也很明顯就不多說了,Proxy的應用場景除了上面說過的MVVM外,還可以用在ORM中,把對象的行為映射到數據庫中,還有數據訪問的代理,總之想用到代理的可以考慮Proxy。
還有就是要記住Proxy不是透明代理,它有自己的this,使用時需要注意。
