<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          ES6 Proxy里面為什么要用Reflect?

          共 5325字,需瀏覽 11分鐘

           ·

          2022-04-15 17:51

          引言

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

          我們可以利用 Proxy 和 Reflect 來實現(xiàn)對于對象的代理劫持操作,類似于 Es 5 中 Object.defineProperty()的效果,

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

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

          前置知識

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

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

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

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

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

          單獨使用 Proxy

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

          const?obj?=?{
          ??name:?'wang.haoyu',
          };

          const?proxy?=?new?Proxy(obj,?{
          ??//?get陷阱中target表示原對象?key表示訪問的屬性名
          ??get(target,?key)?{
          ????console.log('劫持你的數(shù)據(jù)訪問'?+?key);
          ????return?target[key]
          ??},
          });

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

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

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

          Proxy 中的 receiver

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

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

          接下來同樣讓我們以一個簡單的例子來作為切入點:

          const?obj?=?{
          ??name:?'wang.haoyu',
          };

          const?proxy?=?new?Proxy(obj,?{
          ??//?get陷阱中target表示原對象?key表示訪問的屬性名
          ??get(target,?key,?receiver)?{
          ????console.log(receiver?===?proxy);
          ????return?target[key];
          ??},
          });

          //?log:?true
          proxy.name;

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

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

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

          接下來我們來看另外一個例子:

          const?parent?=?{
          ??get?value()?{
          ????return?'19Qingfeng';
          ??},
          };

          const?proxy?=?new?Proxy(parent,?{
          ??//?get陷阱中target表示原對象?key表示訪問的屬性名
          ??get(target,?key,?receiver)?{
          ????console.log(receiver?===?proxy);
          ????return?target[key];
          ??},
          });

          const?obj?=?{
          ??name:?'wang.haoyu',
          };

          //?設(shè)置obj繼承與parent的代理對象proxy
          Object.setPrototypeOf(obj,?proxy);

          //?log:?false
          obj.value

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

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

          那么你可以稍微思考下這里的 receiver 究竟是什么呢?其實這也是 proxy 中 get 陷阱第三個 receiver 存在的意義。

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

          ...
          const?proxy?=?new?Proxy(parent,?{
          ??//?get陷阱中target表示原對象?key表示訪問的屬性名
          ??get(target,?key,?receiver)?{
          -???console.log(receiver?===?proxy)?//?log:false
          +???console.log(receiver?===?obj)?//?log:true
          ????return?target[key];
          ??},
          });
          ...

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

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

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

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

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

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

          比如:

          const?parent?=?{
          ??get?value()?{
          ????return?'19Qingfeng';
          ??},
          };

          const?handler?=?{
          ??get(target,?key,?receiver)?{
          ????console.log(this?===?handler);?//?log:?true
          ????console.log(receiver?===?obj);?//?log:?true
          ????return?target[key];
          ??},
          };

          const?proxy?=?new?Proxy(parent,?handler);

          const?obj?=?{
          ??name:?'wang.haoyu',
          };

          //?設(shè)置obj繼承與parent的代理對象proxy
          Object.setPrototypeOf(obj,?proxy);

          //?log:?false
          obj.value

          Reflect 中的 receiver

          在清楚了 Proxy 中 get 陷阱的 receiver 后,趁熱打鐵我們來聊聊 Reflect 反射 API 中 get 陷阱的 receiver。

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

          const?parent?=?{
          ??name:?'19Qingfeng',
          ??get?value()?{
          ????return?this.name;
          ??},
          };

          const?handler?=?{
          ??get(target,?key,?receiver)?{
          ????return?Reflect.get(target,?key);
          ????//?這里相當(dāng)于?return?target[key]
          ??},
          };

          const?proxy?=?new?Proxy(parent,?handler);

          const?obj?=?{
          ??name:?'wang.haoyu',
          };

          //?設(shè)置obj繼承與parent的代理對象proxy
          Object.setPrototypeOf(obj,?proxy);

          //?log:?false
          console.log(obj.value);

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

          • 當(dāng)我們調(diào)用 obj.value 時,由于 obj 本身不存在 value 屬性。
          • 它繼承的 proxy 對象中存在 value 的屬性訪問操作符,所以會發(fā)生屏蔽效果。
          • 此時會觸發(fā) proxy 上的 get value() 屬性訪問操作。
          • 同時由于訪問了 proxy 上的 value 屬性訪問器,所以此時會觸發(fā) get 陷阱。進入陷阱時,target 為源對象也就是 parent ,key 為 value 。
          • 陷阱中返回 Reflect.get(target,key) 相當(dāng)于 target[key]。
          • 此時,不知不覺中 this 指向在 get 陷阱中被偷偷修改掉了!!原本調(diào)用方的 obj 在陷阱中被修改成為了對應(yīng)的 target 也就是 parent 。
          • 自然而然打印出了對應(yīng)的 parent[value] 也就是 19Qingfeng 。

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

          那么,Relfect 中 get 陷阱的 receiver 就大顯神通了。

          const?parent?=?{
          ??name:?'19Qingfeng',
          ??get?value()?{
          ????return?this.name;
          ??},
          };

          const?handler?=?{
          ??get(target,?key,?receiver)?{
          -???return?Reflect.get(target,?key);
          +???return?Reflect.get(target,?key,?receiver);
          ??},
          };

          const?proxy?=?new?Proxy(parent,?handler);

          const?obj?=?{
          ??name:?'wang.haoyu',
          };

          //?設(shè)置obj繼承與parent的代理對象proxy
          Object.setPrototypeOf(obj,?proxy);

          //?log:?wang.haoyu
          console.log(obj.value);

          上述代碼原理其實非常簡單:

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

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

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

          相信看到這里你已經(jīng)明白 Relfect 中的 receiver 代表的含義是什么了,沒錯它正是可以修改屬性訪問中的 this 指向為傳入的 receiver 對象。

          總結(jié)

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

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

          Proxy 中接受的 Receiver 形參表示代理對象本身或者繼承與代理對象的對象。
          Reflect 中傳遞的 Receiver 實參表示修改執(zhí)行原始操作時的 this 指向。

          來源:https://www.zhihu.com/question/460133198/answer/2416115070


          請你喝杯?? 記得三連哦~

          1.閱讀完記得給?? 醬點個贊哦,有?? 有動力

          2.關(guān)注公眾號前端那些趣事,陪你聊聊前端的趣事

          3.文章收錄在Github?frontendThings?感謝Star?

          瀏覽 106
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  大香蕉伊人成人网 | 青娱乐欧美国产亚洲自拍 | 日本一区二区三区久久久久久久久不卡免费 | 中文字字幕在线中文乱码更新时间 | 国内自拍区 |