ES6語法和知識點:代理proxy
Proxy 可以理解成,在目標物件之前架設一層“攔截”,外界對該物件的訪問,都必須先通過這層攔截,因此提供了一種機制,可以對外界的訪問進行過濾和改寫。
var obj = new Proxy({}, { get: function (target, key, receiver) { console.log(`getting ${key}!`); return Reflect.get(target, key, receiver); }, set: function (target, key, value, receiver) { console.log(`setting ${key}!`); return Reflect.set(target, key, value, receiver); } }); //執行結果 obj.count = 1 // setting count! ++obj.count // getting count! // setting count! // 2
S6 原生提供 Proxy 建構函式,用來生成 Proxy 例項。new Proxy()
表示生成一個Proxy
例項,target
引數表示所要攔截的目標物件,handler
引數也是一個物件,用來定製攔截行為。
created方法:
var object = { proxy: new Proxy(target, handler) }; var proxy = new Proxy({}, { get: function(target, property) { return 35; } }); let obj = Object.create(proxy); obj.time // 35
下面是 Proxy 支援的攔截操作一覽,一共 13 種。
- 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)
。
apply()
apply
方法攔截函式的呼叫、call
和apply
操作。
apply
方法可以接受三個引數,分別是目標物件、目標物件的上下文物件(this
)和目標物件的引數陣列。
get
方法用於攔截某個屬性的讀取操作,可以接受三個引數,依次為目標物件、屬性名和 proxy 例項本身(嚴格地說,是操作行為所針對的物件),其中最後一個引數可選。
set
方法用來攔截某個屬性的賦值操作,可以接受四個引數,依次為目標物件、屬性名、屬性值和 Proxy 例項本身,其中最後一個引數可選。
set
方法的第四個引數receiver
,指的是原始的操作行為所在的那個物件,一般情況下是proxy
例項本身,果目標物件自身的某個屬性,不可寫且不可配置,那麼set
方法將不起作用。
has
方法用來攔截HasProperty
操作,即判斷物件是否具有某個屬性時,這個方construct
方法用於攔截new
命令,下面是攔截物件的寫法。法會生效。典型的操作就是in
運算子。
construct
方法可以接受兩個引數。
target
:目標物件args
:建構函式的引數物件newTarget
:創造例項物件時,new
命令作用的建構函式-
var handler = { construct (target, args, newTarget) { return new target(...args); } };
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
有些原生物件的內部屬性,只有通過正確的this
才能拿到,所以 Proxy 也無法代理這些原生物件的屬性。