什么是proxy?


數組問題 

數組原length為m,當重新設置數組的length為 n,會自動移除數組的最末m-n個元素,只保留起始處的n個元素。

舉個例子,如下:

 

proxy之前,我們無法去模擬數組的這種行為。

什么是Prxoy(代理)和Reflect(反射)?

proxy 是一種封裝,能去攔截並改變js引擎的底層操作,比如一些不可枚舉、不可寫入的屬性。

通過調用new Proxy(),可以創建一個代理去替代另一個對象(目標對象),

這時,代理對目標對象進行了虛擬,因此,該代理和目標對象在表面上可以當做統一對象來看。

Proxy代理允許攔截目標對象的底層操作,而這本來是js引擎的內部操作。

攔截的行為是個函數,可以修改js對象的內置行為,用於響應攔截的特定操作,我們稱為陷阱。

 

Reflect對象是給底層操作提供默認行為的方法的集合,這些操作可以被proxy代理重寫。

每個代理陷阱都有一個對應的反射方法,每個方法與對應的陷阱函數同名,接受的參數也類似。

如果要是使用原先的內置行為,則可以使用對應的反射接口方法。

將代理陷阱和反射方法做了個統一表格,如下:

代理陷阱Proxy

被重寫的行為

默認行為Reflect

get

讀取一個屬性的値

Reflect.get()

set

寫入一個屬性

Reflect.set()

has

in運算符

Reflect.has()

deleteProperty

delete 運算符

Reflect.deleteProperty()

getPrototypeOf

Object.getPrototypeOf()

Reflect.getPrototypeOf()

setPrototypeOf

Object.setPrototypeOf()

Reflect.setPrototypeOf()

isExtensible

Object.isExtensible()

Reflect.isExtensible()

preventExtensions

Object.preventExtensions()

Reflect.preventExtensions()

getOwnPropertyDescriptor

Object.getOwnPropertyDescriptor()

Reflect.getOwnPropertyDescriptor()

defineProperty

Object.defineProperty()

Reflect.defineProperty()

ownKeys

Object.keys() 

Object.getOwnPropertyNames()與 Object.getOwnPropertySymbols()

Reflect.ownKeys()

apply

調用一個函數

Reflect.apply()

construct

使用new調用一個函數

Reflect.construct()

 

創建代理 

var proxy = new Proxy(target, handler);

參數:target參數表示所要攔截的目標對象,

     handler參數也是一個或多個陷阱函數的對象,用來定制攔截行為。若沒有提供陷阱函數,則代理采取默認行為操作。

new Proxy( ) 表示生成一個Proxy實例

let target = {}
let proxyObj = new Proxy(target, {})

proxyObj.name = "proxyName"
console.log(proxyObj.name) // Proxy {name: "proxyName"} 
console.log(target.name)  // {name: "proxyName"} 
console.log(proxyObj.name) //proxyName 
console.log(target.name) //proxyName  

target.name = "targeName" 
console.log(proxyObj.name) //targeName 
console.log(target.name) //targeName

proxy對象將屬性賦值的操作傳遞給target對象;

為target.name設置屬性值,也會在proxy.name上有相同的改變。

get陷阱函數

讀取對象不存在的屬性,會顯示undefined,而不會報錯。
let target = {}
console.log(target.name) // undefined
若我們想讀取對象不存在的屬性,會報錯,該如何設置?

get陷阱函數在讀取屬性時被調用,即使對象不存在此屬性,也可以接受參數。

get陷阱函數有三,分別為

  • trapTarget:被讀取屬性的對象(代理的目標對象)
  • key:被讀取屬性的鍵
  • receiver:操作發生的對象(代理的對象)

注:1)Reflect.get()方法 接受參數和get陷阱函數相同。

  2)set陷阱函數的參數有四個(trapTarget、key、value、receiver), 而get陷阱函數沒有使用value參數,是因為get陷阱函數不需要設置屬性值。

舉個例子來具體說明下: 

eg: 讀取目標屬性不存在的情況下,報錯

var target = {
    name : 'targetName'
} 
let proxy = new Proxy(target, {
  get(trapTarget, key, receiver) {
    if (!(key in trapTarget)) {
      throw new TypeError("屬性" + key + " doesn't exist.");
    }
    return Reflect.get(trapTarget, key, receiver);
  }
})
console.log(proxy.name) // "targetName"
// 添加屬性的功能正常
proxy.place = "北京";
console.log(proxy.place) // "北京"
// 讀取不存在屬性會報錯
console.log(proxy. sex) // 報錯

由於我們是讀取對象的屬性,只需要使用get陷阱函數。

在本例中,通過in運算符來判斷receiver對象上是否存在已有的屬性,從而進行攔截操作。

以上看出,可以添加屬性且能夠讀取存在的屬性,而讀取不存在屬性會報錯。

set陷阱函數

set陷阱函數有四個參數,分別為

  • trapTarget:被接受屬性的對象(代理的目標對象)
  • key:被寫入屬性的鍵
  • value:被寫入屬性的值
  • receiver:操作發生的對象(代理的對象)

set()在寫入屬性成功返回true,否則返回false。

同樣的,Reflect.set()參數和set陷阱函數一致,且Reflect.set()依據操作的不同返回相應的結果。

舉個例子來說明下,

eg: 創建對象,且屬性值只能是num類型,若類型不符,則報錯。需要用set陷阱函數去重新屬性值的默認行為。

let target = {
    name: "target"
}
let proxy = new Proxy(target, {
    set(trapTarget, key, value, receiver) {
      console.log(trapTarget, key, value, receiver)     
    // proxy.count = 1          打印結果為 {name: "target"}           "count"       1       Proxy {name: "target"}       
    // proxy.name = "proxyName” 打印結果為 {name: "target", count: 1} "name"   "proxyName"  Proxy {name: "target", count: 1} 
    // 忽略已有屬性,避免影響它們
        if (!trapTarget.hasOwnProperty(key)) {
             if (isNaN(value)) {
                throw new TypeError("Property must be a number.");
            }
        }
        // 添加屬性
        return Reflect.set(trapTarget, key, value, receiver);
    }
})
// 添加一個新屬性
proxy.count = l
console.log(proxy.count)  // l
console.log(target.count) // l
// 你可以為name 賦一個非數值類型的值,因為該屬性已存在 
proxy.name = "proxy"
console.log(proxy.name)  // "proxyName"
console.log(target.name)  // "proxyName"
// 拋出錯誤
proxy.anotherName = "proxyOtherName" 

當執行 proxy.count = 1set陷阱函數被調用此時trapTarget的値等於target對象,key的値是字符串"count" ’,value的値是1 。

target對象上尚不存在名為count的屬性,因此代理將 value參數傳遞給isNaN()方法進行驗證;

如果驗證結果是NaN ,表示傳入的屬性値不是 一個數値,需要拋出錯誤;

但由於這段代碼將count參數設置為1 ,驗證通過,代理使用一致的四個參數去調用Reflect.set()方法,從而創建了一個新的屬性。

proxy.name被賦值為字符串時,操作成功完成。這是因為target對象已經擁有一個 name屬性

因此驗證時通過調用trapTarget.hasOwnProperty()會忽略該屬性,這就確保允 許在該對象的已有屬性上使用非數値的屬性値。

proxy.anotherName被紙値為字符串時,拋出了一個錯誤。這是因為該對象上並不存在 anotherName屬性,因此該屬性的値必須被驗證,

而因為提供的値不是一個數値,驗證過程 就會拋出錯誤。

has陷阱函數

in 運算符判斷對象是否存在某個屬性,無論該屬性是對象自身屬性,還是其原型屬性

 

 

val是自身屬性,toString原型屬性,

 has陷阱函數參數有兩個,分別為

  • trapTarget:需要讀取屬性的對象(代理的目標對象)
  • key:需要檢查的屬性的鍵

Reflect.has()方法接受與之相同的參數,並向in運算符返回默認響應結果。

使用has 陷阱函數以及Reflect.has()方法,允許你修改部分屬性在接受in檢測時的行為,但保留其他屬性的默認行為。

舉個例子來說明:

eg: 只想要隱藏value屬性

let target = {
    name: "target", value: 42
}
let proxy = new Proxy(target, {
    has(trapTarget, key) {
        if (key === "value") {
            return false
        } else {
            return Reflect.has(trapTarget, key);
        }
    }
})

console.log("value" in proxy); // false
console.log("name" in proxy); // true
console.log("tost ring" in proxy); // true

使用了 has陷牌函數,用於檢查key値是否為"value"。如果是,則 返回false,否則通過調用Reflect.has()方法來返回默認的結果。


免責聲明!

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



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