1.概述(代理器)
Proxy 用于修改某些操作的預設行為,等同于在語言層面做出修改。
Proxy 可以了解成,在目标對象之前架設一層“攔截”,外界對該對象的通路,都必須先通過這層攔截,是以提供了一種機制,可以對外界的通路進行過濾和改寫
Proxy 實際上重載了點運算符,即用自己的定義覆寫了語言的原始定義
ES6 原生提供 Proxy 構造函數,用來生成 Proxy 執行個體
var proxy = new Proxy(target, handler);
Proxy 對象的所有用法,都是上面這種形式,不同的隻是
handler
參數的寫法。其中,
new Proxy()
表示生成一個
Proxy
執行個體,
target
參數表示所要攔截的目标對象,
handler
參數也是一個對象,用來定制攔截行為。如果
handler
沒有設定任何攔截,那就等同于直接通向原對象。
要使得
Proxy
起作用,必須針對
Proxy
執行個體(上例是
proxy
對象)進行操作,而不是針對目标對象(上例是空對象)進行操作。
Proxy支援的攔截操作:
get(target, propKey, receiver):攔截對象屬性的讀取
set(target, propKey, value, receiver):攔截對象屬性的設定
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)
2.Proxy執行個體的方法
2.Proxy執行個體的方法
get方法:用于攔截某個屬性的讀取操作。接受三個參數。目标對象、屬性名和 proxy 執行個體本身(可選)。可以繼承
可以将讀取屬性的操作(get)轉變為執行某個函數,進而實作屬性的鍊式操作。實作一個生成各種DOM節點的通用函數dom
第三個參數總是指向原始的讀操作所在的那個對象。
如果一個屬性不可配置(configurable)且不可寫(writable),則 Proxy 不能修改該屬性,否則通過 Proxy 對象通路該屬性會報錯。
set()方法:用來攔截某個屬性的指派操作,可以接受四個參數,依次為目标對象、屬性名、屬性值和 Proxy 執行個體本身,其中最後一個參數可選.可以用于資料驗證
在對象上面設定内部屬性,屬性名的第一個字元使用下劃線開頭,表示這些屬性不應該被外部使用,結合get和set可以實作。
第四個參數,一般情況下是proxy執行個體本身,但有可能是其他(原始指派行為所在的對象)
如果目标對象自身的某個屬性,不可寫且不可配置,set将不起作用。
嚴格模式下,set代理如果沒有傳回true,就會報錯
apply()方法:攔截函數的調用、
call
和
apply
操作。可以接受三個參數,分别是目标對象、目标對象的上下文對象(
this
)和目标對象的參數數組。每當執行
proxy
函數(直接調用或
call
和
apply
調用),就會被
apply
方法攔截。直接調用
Reflect.apply
方法,也會被攔截。
has()方法:用來攔截
HasProperty
操作,即判斷對象是否具有某個屬性時,這個方法會生效。典型的操作就是
in
運算符。可以接受兩個參數,分别是目标對象、需查詢的屬性名。
如果原對象不可配置或者禁止擴充,這時
has
攔截會報錯。
has方法不判斷一個屬性是對象自身的屬性,還是繼承的屬性
雖然
for...in
循環也用到了
in
運算符,但是
has
攔截對
for...in
循環不生效。
has
攔截隻對
in
運算符生效,對
for...in
循環不生效,導緻不符合要求的屬性沒有被
for...in
循環所排除。
construct()方法:用于攔截new指令,可以接受兩個參數,target目标對象,args構造函數的參數對象,newTarget創造執行個體對象時,new指令作用的構造函數。傳回必須是一個對象。
deleteProperty()用于攔截
delete
操作,如果這個方法抛出錯誤或者傳回
false
,目前屬性就無法被
delete
指令删除。
目标對象自身的不可配置(configurable)的屬性,不能被
deleteProperty
方法删除,否則報錯。
defineProperty
方法攔截了
Object.defineProperty
操作(攔截添加新屬性,不管是直接指派還是用
Object.defineProperty
)
如果目标對象不可擴充(non-extensible),則
defineProperty
不能增加目标對象上不存在的屬性,否則會報錯。另外,如果目标對象的某個屬性不可寫(writable)或不可配置(configurable),則
defineProperty
方法不得改變這兩個設定
getOwnPropertyDescriptor
方法攔截
Object.getOwnPropertyDescriptor()
,傳回一個屬性描述對象或者
undefined
getPrototypeOf
方法主要用來攔截擷取對象原型,攔截以下操作
-
Object.prototype.__proto__
-
Object.prototype.isPrototypeOf()
-
Object.getPrototypeOf()
-
Reflect.getPrototypeOf()
-
instanceof
傳回值必須是對象或者
null
,否則報錯,如果目标對象不可擴充(non-extensible),
getPrototypeOf
方法必須傳回目标對象的原型對象。
isExtensible
方法攔截
Object.isExtensible
操作該方法,隻能傳回布爾值,否則傳回值會被自動轉為布爾值
它的傳回值必須與目标對象的isExtensible屬性保持一緻。
ownKeys
方法用來攔截對象自身屬性的讀取操作,攔截以下操作:
-
Object.getOwnPropertyNames()
-
Object.getOwnPropertySymbols()
-
Object.keys()
-
循環for...in
使用
Object.keys
方法時,有三類屬性會被
ownKeys
方法自動過濾,不會傳回。
- 目标對象上不存在的屬性
- 屬性名為 Symbol 值
- 不可周遊(
)的屬性enumerable
ownKeys
方法傳回的數組成員,隻能是字元串或 Symbol 值。如果有其他類型的值,或者傳回的根本不是數組,就會報錯。
如果目标對象自身包含不可配置的屬性,則該屬性必須被
ownKeys
方法傳回,否則報錯
preventExtensions
方法攔截
Object.preventExtensions()
。該方法必須傳回一個布爾值,否則會被自動轉為布爾值。
proxy.preventExtensions
方法傳回
true
,但這時
Object.isExtensible(proxy)
會傳回
true
,是以報錯。
為了防止出現這個問題,通常要在
proxy.preventExtensions
方法裡面,調用一次
Object.preventExtensions
。
setPrototypeOf
方法主要用來攔截
Object.setPrototypeOf
方法。
該方法隻能傳回布爾值,否則會被自動轉為布爾值。另外,如果目标對象不可擴充(non-extensible),
setPrototypeOf
方法不得改變目标對象的原型。
3.Proxy.revocable() 方法
傳回一個可取消的Proxy執行個體。
Proxy.revocable
方法傳回一個對象,該對象的
proxy
屬性是
Proxy
執行個體,
revoke
屬性是一個函數,可以取消
Proxy
執行個體。上面代碼中,當執行
revoke
函數之後,再通路
Proxy
執行個體,就會抛出一個錯誤。
Proxy.revocable
的一個使用場景是,目标對象不允許直接通路,必須通過代理通路,一旦通路結束,就收回代理權,不允許再次通路。
4.this問題
在 Proxy 代理的情況下,目标對象内部的
this
關鍵字會指向 Proxy 代理。
有些原生對象的内部屬性,隻有通過正确的
this
才能拿到,是以 Proxy 也無法代理這些原生對象的屬性。
this
綁定原始對象,就可以解決這個問題
5.執行個體:web服務的用戶端
Proxy 對象可以攔截目标對象的任意屬性,這使得它很合适用來寫 Web 服務的用戶端。