從C#到TypeScript - Proxy


總目錄

從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,使用時需要注意。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM