您好,登錄后才能下訂單哦!
這篇文章主要介紹了ES6中的Proxy類(lèi)如何使用的相關(guān)知識(shí),內(nèi)容詳細(xì)易懂,操作簡(jiǎn)單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇ES6中的Proxy類(lèi)如何使用文章都會(huì)有所收獲,下面我們一起來(lái)看看吧。
在 ES5
中,我們可以定義一個(gè)對(duì)象,并且對(duì)其進(jìn)行操作(添加或查找),如下代碼所示:
const moment = { age: 18, address: "西安", }; moment["hobby"] = "basketball"; moment.hight = 1.59; console.log(moment.age); // 18
那如果我們要對(duì)該對(duì)象進(jìn)行監(jiān)聽(tīng)呢,我們希望可以監(jiān)聽(tīng)到這個(gè)對(duì)象中的屬性被設(shè)置或獲取的過(guò)程,我們可以通過(guò)屬性描述符中的存儲(chǔ)屬性來(lái)做到這個(gè)功能。
在 ES5
中,所有的屬性都具備了屬性描述符,具體使用如下圖所示:
就像上圖所展示的一樣,這個(gè)普通的對(duì)象屬性對(duì)應(yīng)的屬性描述符,可不僅僅是一個(gè)18,它還包含另外三個(gè)特性,它們分別是:
writable
;
enumerable
;
configurable
;
在創(chuàng)建普通戶(hù)型時(shí),屬性描述符會(huì)使用默認(rèn)值,我們可以使用 Object.defineProperty(...)
來(lái)添加一個(gè)新屬性或者修改一個(gè)已有屬性(當(dāng)該屬性 Configurable
的值為 true
時(shí))并對(duì)特性進(jìn)行設(shè)置,具體如下代碼所示:
const moment = { age: 18, address: "西安", }; Object.defineProperty(moment, "address", { value: "肇慶", writable: true, configurable: true, enumerable: true, }); console.log(moment.address); // 肇慶
該方法接收三個(gè)參數(shù),它們分別為:
要定義屬性的對(duì)象;
要定義或修改的屬性的名稱(chēng)或 Symbol
;
要定義或修改的屬性描述符;
前面說(shuō)了這么多基礎(chǔ)的東西,但是還沒(méi)有講解是怎么接收到屬性的變化的,在這里,屬性描述符 Object.defineProperty
提供了兩個(gè)屬性,它們分別是 set
和 get
,兩個(gè)屬性的使用如下所示:
const moment = { age: 18, address: "西安", }; Object.keys(moment).forEach((key) => { let value = moment[key]; Object.defineProperty(moment, key, { get: function () { console.log(`監(jiān)聽(tīng)到了對(duì)象的 ${key} 屬性被訪問(wèn)了`); return value; }, set: function (params) { console.log(`監(jiān)聽(tīng)到了對(duì)象的 ${key} 屬性被修改了`); value = params; }, }); }); moment.age = 22; const foo = moment.address;
當(dāng)我們對(duì) moment.age
進(jìn)行賦值的時(shí)候,會(huì)調(diào)用 set
屬性上的方法,最終會(huì)輸出 監(jiān)聽(tīng)到了對(duì)象的 age 屬性被修改了
。
當(dāng)我們對(duì) moment.address
進(jìn)行取值的時(shí)候,會(huì)調(diào)用 get
屬性上的方法,最終會(huì)輸出 監(jiān)聽(tīng)到了對(duì)象的 address 屬性被訪問(wèn)了
。
雖然這兩個(gè)方法是能做到,但是如果我們想監(jiān)聽(tīng)更加豐富的操作的時(shí)候是做不到的,例如新增屬性,淡出屬性,使用 Object.defineProperty(...)
是做不到的
最終結(jié)果如上圖所示,終端中的輸出是對(duì)應(yīng)上面畫(huà)圈圈的代碼執(zhí)行。
那么 Proxy
的出現(xiàn)就很好的解決了這一痛點(diǎn)。
在 ES6
中,新增了一個(gè) Proxy
類(lèi),這個(gè)類(lèi)從名字就可以看出來(lái),是用于幫助我們創(chuàng)建一個(gè)代理的,它是一種由你創(chuàng)建的特殊對(duì)象,它封裝另一個(gè)普通對(duì)象或者說(shuō)擋在這個(gè)普通對(duì)象的前面,例如你要修改一個(gè)對(duì)象,它主要有以下的流程圖:
它就像一個(gè)關(guān)卡對(duì)你的操作進(jìn)行攔截。
這個(gè)類(lèi)的基本使用如下所示:
const moment = { age: 18, address: "西安", }; const proxy = new Proxy(moment, {}); console.log(proxy); // { age: 18, address: '西安' }
你會(huì)發(fā)現(xiàn)它返回的是同一個(gè)對(duì)象,但是內(nèi)存地址不同,所以通過(guò)嚴(yán)格相等比較返回的值也就是 false
。
Peoxy
總共有13個(gè)捕獲器,但是常用的就那么幾個(gè),這里不低其全部講解,具體可以查看 MDN官網(wǎng)
話不多說(shuō),直接上代碼:
const moment = { age: 18, address: "西安", }; function foo(x, y) { return x + y; } const proxy = new Proxy(moment, { has: function (target, prop) { console.log(`使用 in 訪問(wèn)了 ${prop} 是否存在于 moment 對(duì)象`); }, get: function (target, property, receiver) { console.log(`通過(guò)讀取 moment 對(duì)象中的 ${property} 屬性`); }, set: function (target, property, value, receiver) { console.log(`通過(guò)設(shè)置 moment 對(duì)象中的 ${property} 屬性為 ${value}`); }, }); const fProxy = new Proxy(foo, { apply: function (target, _, params) { return target(...params) * 10; }, construct: function (target, argumentsList, newTarget) { console.log(target); // [Function: foo] console.log(argumentsList); // [ 1, 2 ] console.log(newTarget); // [Function: foo] return {}; }, }); "age" in proxy; // 使用 in 訪問(wèn)了 age 是否存在于 moment 對(duì)象 proxy.age; // 通過(guò)讀取 moment 對(duì)象中的 age 屬性 proxy.address = "肇慶"; // 通過(guò)設(shè)置 moment 對(duì)象中的 address 屬性為 肇慶 console.log(foo(1, 2)); // 3 console.log(fProxy(1, 2)); // 30 new fProxy(1, 2);
在上面的代碼中,target === moment
和 receiver === proxy
都返回 true
,也就是說(shuō) target
為對(duì)應(yīng)要修改的對(duì)象,而 receiver
為 Proxy
或者繼承 Proxy
的對(duì)象。
操作 Proxy
的同時(shí)會(huì)修改 moment
對(duì)象。
普通對(duì)象總是陷入到目標(biāo)對(duì)象,并且在創(chuàng)建之后不能改變,只要還保持著對(duì)這個(gè)代理的引用,代理的機(jī)制就將維持下去。
但是可能會(huì)存在這樣的情況,比如你想要?jiǎng)?chuàng)建一個(gè)在你想要停止它作為代理時(shí)便可被停用的代理,解決的方案是創(chuàng)建可取消代理,具體代碼如下所示:
const moment = { age: 18, address: "西安", }; const { proxy, revoke } = Proxy.revocable(moment, { get: function (target, key, receiver) { console.log("get 捕獲器"); }, }); proxy.address; revoke(); proxy.address;
最終的輸出如下圖所示:
一旦可取消代理被取消,任何對(duì)他的訪問(wèn)都會(huì)拋出 TypeError
錯(cuò)誤。
盡管現(xiàn)在 Proxy
已經(jīng)做得很好了,但是在某些情況下,代理也不能與現(xiàn)在的 ECMAScript
機(jī)制很好地協(xié)同。
Peoxy
潛在的一個(gè)問(wèn)題來(lái)源是 this
值,我們知道方法中的 this
通常執(zhí)行調(diào)用這個(gè)方法的對(duì)象,具體代碼如下所示:
const target = { moment() { return this === proxy; }, }; const proxy = new Proxy(target, {}); console.log(target.moment()); // false console.log(proxy.moment()); // true
按照正常的理解這是沒(méi)有問(wèn)題的調(diào)用 Proxy
上的任何方法,而這個(gè)方法進(jìn)而又會(huì)調(diào)用另一個(gè)方法,實(shí)際上都會(huì)調(diào)用 Proxy
內(nèi)部的方法,這是符合預(yù)期的行為,但是,如果目標(biāo)對(duì)象依賴(lài)于對(duì)象表示,那就可能碰到意料之外的問(wèn)題。
舉個(gè)例子:
const wm = new WeakMap(); class User { constructor(userId) { wm.set(this, userId); } set id(userId) { wm.set(this, userId); } get id() { return wm.get(this); } }
由于這個(gè)實(shí)現(xiàn)依賴(lài) User
實(shí)例的對(duì)象標(biāo)識(shí),在這個(gè)實(shí)例被代理的情況下就會(huì)出現(xiàn)問(wèn)題:
const user = new User(1); console.log(user.id); // 1 const proxy = new Proxy(user, {}); console.log(proxy.id); // undefined
這是因?yàn)?User
實(shí)例一開(kāi)始使用目標(biāo)對(duì)象作為 WeakMap
的鍵,代理對(duì)象卻嘗試從自身取得這個(gè)實(shí)例,要解決這個(gè)問(wèn)題,就需要重新設(shè)置代理,把代理 User
實(shí)例改為代理 User
類(lèi)本身,之后再創(chuàng)建代理的實(shí)例就會(huì)創(chuàng)建代理實(shí)例作為 WeakMap
的鍵了:
const UserProcess = new Proxy(User, {}); const proxy = new UserProcess(1); console.log(proxy.id); // 1
代理與內(nèi)置引用類(lèi)型的實(shí)例通??梢院芎玫貐f(xié)同,但有些 ECMAScript
內(nèi)置類(lèi)型可能會(huì)依賴(lài)代理無(wú)法控制的機(jī)制,結(jié)果導(dǎo)致在代理上調(diào)用某些方法會(huì)出錯(cuò),具體代碼如下所示:
const target = new Date(); const proxy = new Proxy(target, {}); console.log(proxy instanceof Date); // true proxy.getDate(); // TypeError: this is not a Date object.
在上面的代碼中,根據(jù) ECMAScript
規(guī)范,Date
類(lèi)型方法的執(zhí)行依賴(lài) this
值上的內(nèi)部曹偉 [[NumberDate]]
,代理對(duì)象上不存在這個(gè)內(nèi)部槽位,而且這個(gè)內(nèi)部槽位的值也不能通過(guò)普通的 get()
和 set()
操作訪問(wèn)到,于是代理攔截后本應(yīng)該轉(zhuǎn)發(fā)給目標(biāo)對(duì)象的方法會(huì)拋出 TypeRrror
的錯(cuò)誤。
關(guān)于“ES6中的Proxy類(lèi)如何使用”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對(duì)“ES6中的Proxy類(lèi)如何使用”知識(shí)都有一定的了解,大家如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。