Proxy概述
Proxy 用於修改某些操作的默認行爲,等同於在語言層面做出修改,所以屬於一種“元編程”(meta programming),即對編程語言進行編程。
Proxy 可以理解成,在目標對象之前架設一層“攔截”,外界對該對象的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。Proxy 這個詞的原意是代理,用在這裏表示由它來“代理”某些操作,可以譯爲“代理器”。
Proxy用法
const proxy = new Proxy(target, handler); // new Proxy()
表示生成一個Proxy
實例,target
參數表示所要攔截的目標對象,handler
參數也是一個對象,用來定製攔截行爲。
Proxy支持的攔截操作
參數說明:target指目標對象, propKey指屬性名稱,receiver指Proxy實例
- get(target, propKey, receiver):攔截對象屬性的讀取,比如
proxy.foo
和proxy['foo']
。 - set(target, propKey, value, receiver):攔截對象屬性的設置,比如
proxy.foo = v
或proxy['foo'] = v
,返回一個布爾值。 - has(target, propKey):攔截
propKey in proxy
的操作,返回一個布爾值。 - deleteProperty(target, propKey):攔截
delete proxy[propKey]
的操作,返回一個布爾值。 - ownKeys(target):攔截
Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in
循環,返回一個數組。該方法返回目標對象所有自身的屬性的屬性名,而Object.keys()
的返回結果僅包括目標對象自身的可遍歷屬性。 - getOwnPropertyDescriptor(target, propKey):攔截
Object.getOwnPropertyDescriptor(proxy, propKey)
,返回屬性的描述對象。 - defineProperty(target, propKey, propDesc):攔截
Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一個布爾值。 - preventExtensions(target):攔截
Object.preventExtensions(proxy)
,返回一個布爾值。 - getPrototypeOf(target):攔截
Object.getPrototypeOf(proxy)
,返回一個對象。 - isExtensible(target):攔截
Object.isExtensible(proxy)
,返回一個布爾值。 - setPrototypeOf(target, proto):攔截
Object.setPrototypeOf(proxy, proto)
,返回一個布爾值。如果目標對象是函數,那麼還有兩種額外操作可以攔截。 - apply(target, object, args):攔截 Proxy 實例作爲函數調用的操作,比如
proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。 - construct(target, args):攔截 Proxy 實例作爲構造函數調用的操作,比如
new proxy(...args)
。
Proxy.revocable()
Proxy.revocable
方法返回一個可取消的 Proxy 實例。
let target = {};
let handler = {};
let {proxy, revoke} = Proxy.revocable(target, handler);
proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked
Proxy.revocable
方法返回一個對象,該對象的proxy
屬性是Proxy
實例,revoke
屬性是一個函數,可以取消Proxy
實例。上面代碼中,當執行revoke
函數之後,再訪問Proxy
實例,就會拋出一個錯誤。
Proxy.revocable
的一個使用場景是,目標對象不允許直接訪問,必須通過代理訪問,一旦訪問結束,就收回代理權,不允許再次訪問。
this 問題
雖然 Proxy 可以代理針對目標對象的訪問,但它不是目標對象的透明代理,即不做任何攔截的情況下,也無法保證與目標對象的行爲一致。主要原因就是在 Proxy 代理的情況下,目標對象內部的this
關鍵字會指向 Proxy 代理。
const target = {
m: function () {
console.log(this === proxy);
}
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m() // true