天天看點

es6學習筆記3:Proxy

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執行個體的方法

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 服務的用戶端。