js中為什么Proxy一定要配合Reflect使用_第1頁
js中為什么Proxy一定要配合Reflect使用_第2頁
js中為什么Proxy一定要配合Reflect使用_第3頁
js中為什么Proxy一定要配合Reflect使用_第4頁
js中為什么Proxy一定要配合Reflect使用_第5頁
已閱讀5頁,還剩4頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

第js中為什么Proxy一定要配合Reflect使用目錄引言前置知識單獨(dú)使用ProxyProxy中的receiverReflect中的receiver總結(jié)結(jié)尾

引言

EcmaScript2015中引入了Proxy代理與Reflect反射兩個(gè)新的內(nèi)置模塊。

我們可以利用Proxy和Reflect來實(shí)現(xiàn)對于對象的代理劫持操作,類似于Es5中Object.defineProperty()的效果,不過ReflectProxy遠(yuǎn)遠(yuǎn)比它強(qiáng)大。

大多數(shù)開發(fā)者都了解這兩個(gè)Es6中的新增內(nèi)置模塊,可是你也許并不清楚為什么Proxy一定要配合Reflect使用。

這里,文章通過幾個(gè)通俗易懂的例子來講述它們之間相輔相成的關(guān)系。

前置知識

Proxy代理,它內(nèi)置了一系列陷阱用于創(chuàng)建一個(gè)對象的代理,從而實(shí)現(xiàn)基本操作的攔截和自定義(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)。

Reflect反射,它提供攔截JavaScript操作的方法。這些方法與Proxy的方法相同。

簡單來說,我們可以通過Proxy創(chuàng)建對于原始對象的代理對象,從而在代理對象中使用Reflect達(dá)到對于JavaScript原始操作的攔截。

如果你還不了解,那么趕快去MDN上去補(bǔ)習(xí)他們的知識吧。

畢竟大名鼎鼎的VueJs/Core中核心的響應(yīng)式模塊就是基于這兩個(gè)Api來實(shí)現(xiàn)的。

單獨(dú)使用Proxy

開始的第一個(gè)例子,我們先單獨(dú)使用Proxy來烹飪一道簡單的開胃小菜:

constobj={

name:'wang.haoyu',

constproxy=newProxy(obj,{

//get陷阱中target表示原對象key表示訪問的屬性名

get(target,key){

console.log('劫持你的數(shù)據(jù)訪問'+key);

returntarget[key]

//劫持你的數(shù)據(jù)訪問name-wang.haoyu

看起來很簡單對吧,我們通過Proxy創(chuàng)建了一個(gè)基于obj對象的代理,同時(shí)在Proxy中聲明了一個(gè)get陷阱。

當(dāng)訪問我們訪問時(shí)實(shí)際觸發(fā)了對應(yīng)的get陷阱,它會執(zhí)行g(shù)et陷阱中的邏輯,同時(shí)會執(zhí)行對應(yīng)陷阱中的邏輯,最終返回對應(yīng)的target[key]也就是所謂的wang.haoyu.

Proxy中的receiver

上邊的Demo中一切都看起來順風(fēng)順?biāo)疀]錯吧,細(xì)心的同學(xué)在閱讀Proxy的MDN文檔上可能會發(fā)現(xiàn)其實(shí)Proxy中g(shù)et陷阱中還會存在一個(gè)額外的參數(shù)receiver。

那么這里的receiver究竟表示什么意思呢?大多數(shù)同學(xué)會將它理解成為代理對象,但這是不全面的。

接下來同樣讓我們以一個(gè)簡單的例子來作為切入點(diǎn):

constobj={

name:'wang.haoyu',

constproxy=newProxy(obj,{

//get陷阱中target表示原對象key表示訪問的屬性名

get(target,key,receiver){

console.log(receiver===proxy);

returntarget[key];

//log:true

;

上述的例子中,我們在Proxy實(shí)例對象的get陷阱上接收了receiver這個(gè)參數(shù)。

同時(shí),我們在陷阱內(nèi)部打印console.log(receiver===proxy);它會打印出true,表示這里receiver的確是和代理對象相等的。

所以receiver的確是可以表示代理對象,但是這僅僅是receiver代表的一種情況而已。

接下來我們來看另外一個(gè)例子:

constparent={

getvalue(){

return'19Qingfeng';

constproxy=newProxy(parent,{

//get陷阱中target表示原對象key表示訪問的屬性名

get(target,key,receiver){

console.log(receiver===proxy);

returntarget[key];

constobj={

name:'wang.haoyu',

//設(shè)置obj繼承與parent的代理對象proxy

Object.setPrototypeOf(obj,proxy);

//log:false

obj.value

關(guān)于原型上出現(xiàn)的get/set屬性訪問器的屏蔽效果,我在這篇文章中進(jìn)行了詳細(xì)闡述。這里我就不展開講解了。

我們可以看到,上述的代碼同樣我在proxy對象的get陷阱上打印了console.log(receiver===proxy);結(jié)果卻是false。

那么你可以稍微思考下這里的receiver究竟是什么呢?其實(shí)這也是proxy中g(shù)et陷阱第三個(gè)receiver存在的意義。

它是為了傳遞正確的調(diào)用者指向,你可以看看下方的代碼:

...

constproxy=newProxy(parent,{

//get陷阱中target表示原對象key表示訪問的屬性名

get(target,key,receiver){

-console.log(receiver===proxy)//log:false

+console.log(receiver===obj)//log:true

returntarget[key];

...

其實(shí)簡單來說,get陷阱中的receiver存在的意義就是為了正確的在陷阱中傳遞上下文。

涉及到屬性訪問時(shí),不要忘記get陷阱還會觸發(fā)對應(yīng)的屬性訪問器,也就是所謂的get訪問器方法。

我們可以清楚的看到上述的receiver代表的是繼承與Proxy的對象,也就是obj。

看到這里,我們明白了Proxy中g(shù)et陷阱的receiver不僅僅代表的是Proxy代理對象本身,同時(shí)也許他會代表繼承Proxy的那個(gè)對象。

其實(shí)本質(zhì)上來說它還是為了確保陷阱函數(shù)中調(diào)用者的正確的上下文訪問,比如這里的receiver指向的是obj。

當(dāng)然,你不要將revceiver和get陷阱中的this弄混了,陷阱中的this關(guān)鍵字表示的是代理的handler對象。

比如:

constparent={

getvalue(){

return'19Qingfeng';

consthandler={

get(target,key,receiver){

console.log(this===handler);//log:true

console.log(receiver===obj);//log:true

returntarget[key];

constproxy=newProxy(parent,handler);

constobj={

name:'wang.haoyu',

//設(shè)置obj繼承與parent的代理對象proxy

Object.setPrototypeOf(obj,proxy);

//log:false

obj.value

Reflect中的receiver

在清楚了Proxy中g(shù)et陷阱的receiver后,趁熱打鐵我們來聊聊Reflect反射API中g(shù)et陷阱的receiver。

我們知道在Proxy中(以下我們都以get陷阱為例)第三個(gè)參數(shù)receiver代表的是代理對象本身或者繼承與代理對象的對象,它表示觸發(fā)陷阱時(shí)正確的上下文。

constparent={

name:'19Qingfeng',

getvalue(){

return;

consthandler={

get(target,key,receiver){

returnReflect.get(target,key);

//這里相當(dāng)于returntarget[key]

constproxy=newProxy(parent,handler);

constobj={

name:'wang.haoyu',

//設(shè)置obj繼承與parent的代理對象proxy

Object.setPrototypeOf(obj,proxy);

//log:false

console.log(obj.value);

我們稍微分析下上邊的代碼:

當(dāng)我們調(diào)用obj.value時(shí),由于obj本身不存在value屬性。

它繼承的proxy對象中存在value的屬性訪問操作符,所以會發(fā)生屏蔽效果。

此時(shí)會觸發(fā)proxy上的getvalue()屬性訪問操作。

同時(shí)由于訪問了proxy上的value屬性訪問器,所以此時(shí)會觸發(fā)get陷阱。

進(jìn)入陷阱時(shí),target為源對象也就是parent,key為value。

陷阱中返回Reflect.get(target,key)相當(dāng)于target[key]。

此時(shí),不知不覺中this指向在get陷阱中被偷偷修改掉了??!

原本調(diào)用方的obj在陷阱中被修改成為了對應(yīng)的target也就是parent。

自然而然打印出了對應(yīng)的parent[value]也就是19Qingfeng。

這顯然不是我們期望的結(jié)果,當(dāng)我訪問obj.value時(shí),我希望應(yīng)該正確輸出對應(yīng)的自身上的name屬性也就是所謂的obj.value=wang.haoyu。

那么,Relfect中g(shù)et陷阱的receiver就大顯神通了。

constparent={

name:'19Qingfeng',

getvalue(){

return;

consthandler={

get(target,key,receiver){

-returnReflect.get(target,key);

+returnReflect.get(target,key,receiver);

constproxy=newProxy(parent,handler);

constobj={

name:'wang.haoyu',

//設(shè)置obj繼承與parent的代理對象proxy

Object.setPrototypeOf(obj,proxy);

//log:wang.haoyu

console.log(obj.value);

上述代碼原理其實(shí)非常簡單:

首先,之前我們提到過在Proxy中g(shù)et陷阱的receiver不僅僅會表示代理對象本身同時(shí)也還有可能表示繼承于代理對象的對象,具體需要區(qū)別與調(diào)用方。這里顯然它是指向繼承與代理對象的obj。

其次,我們在Reflect中g(shù)et陷阱中第三個(gè)參數(shù)傳遞了Proxy中的receiver也就是obj作為形參,它會修改調(diào)用時(shí)的this指向。

你可以簡單的將Reflect.get(target,key,receiver)理解成為target[key].call(receiver),不過這是一段偽代碼,但是這樣你可能更好理解。

相信看到這里你已經(jīng)明白Relfect中的receiver代表的含義是什么了,沒錯它正是可以修改屬性訪問中的this指向?yàn)閭魅氲膔eceiver對象。

總結(jié)

相信看到這里大家都已經(jīng)明白了,為什么Proxy一定要配合Reflect使用。恰恰是為什么觸發(fā)代理對象的劫持時(shí)保證正確的this上下文指向。

我們再來稍稍回憶一下,針對于get陷阱(當(dāng)然set其他之類涉及到receiver的陷阱同理):

Proxy中接受的Receiver形參表示代理對象本身或者繼承與代理對象的對象。

Reflect中傳遞的Receiver實(shí)參表示修改執(zhí)行原始操作時(shí)的this指向。

結(jié)尾

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論