精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

聊聊你不知道的 Proxy

開(kāi)發(fā) 前端
在日常工作中,相信挺多小伙伴都用過(guò) Web 調(diào)試代理工具,比如 Fiddler 或 Charles。通過(guò)使用 Web 調(diào)試代理工具,我們可以抓取 HTTP/HTTPS 協(xié)議請(qǐng)求,還可以手動(dòng)修改請(qǐng)求參數(shù)和響應(yīng)結(jié)果。

 [[379873]]

本文轉(zhuǎn)載自微信公眾號(hào)「全棧修仙之路」,作者阿寶哥。轉(zhuǎn)載本文請(qǐng)聯(lián)系全棧修仙之路公眾號(hào)。  

阿寶哥將從 6 個(gè)方面入手,帶你一步一步揭開(kāi) Proxy 對(duì)象的神秘面紗。閱讀完本文,你將了解以下內(nèi)容:

  • 代理的作用;
  • Proxy 對(duì)象與 Reflect 對(duì)象的相關(guān)知識(shí);
  • Proxy 對(duì)象的 6 個(gè)使用場(chǎng)景;
  • 使用 Proxy 對(duì)象時(shí)的一些注意事項(xiàng);
  • Proxy 在開(kāi)源項(xiàng)目中的應(yīng)用。

一、聊一聊代理

在日常工作中,相信挺多小伙伴都用過(guò) Web 調(diào)試代理工具,比如 Fiddler 或 Charles。通過(guò)使用 Web 調(diào)試代理工具,我們可以抓取 HTTP/HTTPS 協(xié)議請(qǐng)求,還可以手動(dòng)修改請(qǐng)求參數(shù)和響應(yīng)結(jié)果。不僅如此,在調(diào)試線上問(wèn)題時(shí),利用 Web 調(diào)試代理工具,你還可以把線上壓縮混淆過(guò) 的 JS 文件映射成本地 未壓縮混淆過(guò) 的 JS 文件。

在簡(jiǎn)單介紹了 Web 調(diào)試代理工具的基本功能之后,我們來(lái)看一下使用 Web 調(diào)試代理工具的 HTTP 請(qǐng)求流程:


 

 

通過(guò)上圖可知,在引入 Web 調(diào)試代理工具之后,我們發(fā)起的 HTTP 請(qǐng)求都會(huì)通過(guò) Web Proxy 進(jìn)行轉(zhuǎn)發(fā)和處理。增加了 Web Proxy 代理層,讓我們能夠更好地控制 HTTP 請(qǐng)求的流程。對(duì)于單頁(yè)應(yīng)用程序來(lái)說(shuō),當(dāng)從服務(wù)器獲取數(shù)據(jù)之后,我們就會(huì)讀取相應(yīng)的數(shù)據(jù)在頁(yè)面上顯示出來(lái):

 

以上流程與瀏覽器直接從服務(wù)器獲取數(shù)據(jù)類(lèi)似:

 

為了能夠靈活控制 HTTP 請(qǐng)求的流程,我們?cè)黾恿说?Web Proxy 層。那么我們能否控制數(shù)據(jù)對(duì)象的讀取流程呢?答案是可以的,我們可以利用 Web API,比如 Object.defineProperty 或 Proxy API。在引入 Web API 之后,數(shù)據(jù)的訪問(wèn)流程如下圖所示:

 

接下來(lái),阿寶哥將重點(diǎn)介紹 Proxy API,它可是 Vue3 實(shí)現(xiàn)數(shù)據(jù)響應(yīng)式幕后的 “功臣” 喲。對(duì)它感興趣的小伙伴,跟阿寶哥一起學(xué)起來(lái)吧。

二、Proxy 對(duì)象簡(jiǎn)介

Proxy 對(duì)象用于創(chuàng)建一個(gè)對(duì)象的代理,從而實(shí)現(xiàn)基本操作的攔截和自定義(如屬性查找、賦值、枚舉、函數(shù)調(diào)用等)。Proxy 的構(gòu)造函數(shù)語(yǔ)法為:

  1. const p = new Proxy(target, handler) 

相關(guān)的參數(shù)說(shuō)明如下:

  • target:要使用 Proxy 包裝的目標(biāo)對(duì)象(可以是任何類(lèi)型的對(duì)象,包括原生數(shù)組,函數(shù),甚至另一個(gè)代理)。
  • handler:一個(gè)通常以函數(shù)作為屬性的對(duì)象,各屬性中的函數(shù)分別定義了在執(zhí)行各種操作時(shí)代理 p 的行為。

在介紹 Proxy 對(duì)象的使用示例前,我們先來(lái)了解一下它的兼容性:

 

(圖片來(lái)源:https://caniuse.com/?search=Proxy)

由上圖可知,Proxy API 的兼容性并不是很好,所以大家在使用的時(shí)候要注意其兼容性問(wèn)題。

2.1 Proxy 對(duì)象使用示例

了解完 Proxy 構(gòu)造函數(shù),我們來(lái)看一個(gè)簡(jiǎn)單的例子:

  1. const man = { 
  2.   name"阿寶哥"
  3. }; 
  4.  
  5. const proxy = new Proxy(man, { 
  6.   get(target, property, receiver) { 
  7.     console.log(`正在訪問(wèn)${property}屬性`); 
  8.     return target[property]; 
  9.   }, 
  10. }); 
  11.  
  12. console.log(proxy.name); 
  13. console.log(proxy.age); 

在以上示例中,我們使用了 Proxy 構(gòu)造函數(shù)為 man 對(duì)象,創(chuàng)建了一個(gè)代理對(duì)象。在創(chuàng)建代理對(duì)象時(shí),我們定義了一個(gè) get 捕獲器,用于捕獲屬性讀取的操作。 捕獲器的作用就是用于攔截用戶對(duì)目標(biāo)對(duì)象的相關(guān)操作,在這些操作傳播到目標(biāo)對(duì)象之前,會(huì)先調(diào)用對(duì)應(yīng)的捕獲器函數(shù),從而攔截并修改相應(yīng)的行為。

在設(shè)置了 get 捕獲器之后,當(dāng)成功運(yùn)行以上的示例代碼,控制臺(tái)會(huì)輸出以下結(jié)果:

  1. 正在訪問(wèn)name屬性 
  2. 阿寶哥 
  3. 正在訪問(wèn)age屬性 
  4. undefined 

通過(guò)觀察以上輸出結(jié)果,我們可以發(fā)現(xiàn) get 捕獲器 不僅可以攔截已知屬性的讀取操作,也可以攔截未知屬性的讀取操作。在創(chuàng)建 Proxy 對(duì)象時(shí),除了定義 get 捕獲器 之外,我們還可以定義其他的捕獲器,比如 has、set、delete、apply 或 ownKeys 等。

2.2 handler 對(duì)象支持的捕獲器

handler 對(duì)象支持 13 種捕獲器,這里阿寶哥只列舉以下 5 種常用的捕獲器:

  • handler.get():屬性讀取操作的捕獲器。
  • handler.set():屬性設(shè)置操作的捕獲器。
  • handler.deleteProperty():delete 操作符的捕獲器。
  • handler.ownKeys():Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕獲器。
  • handler.has():in 操作符的捕獲器。

需要注意的是,所有的捕獲器是可選的。如果沒(méi)有定義某個(gè)捕獲器,那么就會(huì)保留源對(duì)象的默認(rèn)行為。 看完上面的捕獲器介紹,是不是覺(jué)得 Proxy 對(duì)象很強(qiáng)大。

三、Reflect 對(duì)象簡(jiǎn)介

Reflect 是一個(gè)內(nèi)置的對(duì)象,它提供攔截 JavaScript 操作的方法。這些方法與 proxy handlers 的方法相同。Reflect 不是一個(gè)函數(shù)對(duì)象,因此它是不可構(gòu)造的。

在介紹 Reflect 對(duì)象的使用示例前,我們先來(lái)了解一下它的兼容性:

 

(圖片來(lái)源:https://caniuse.com/?search=Reflect)

3.1 Reflect 對(duì)象使用示例

  1. const man = { 
  2.   name"阿寶哥"
  3.   city: "Xiamen"
  4. }; 
  5.  
  6. console.log(Reflect.set(man, "sex", 1)); // true 
  7. console.log(Reflect.has(man, "name")); // true 
  8. console.log(Reflect.has(man, "age")); // false 
  9. console.log(Reflect.ownKeys(man)); // [ 'name''city''sex' ] 

除了示例中介紹的 set、has 和 ownKeys 方法之外,Reflect 對(duì)象還支持 get、defineProperty 和 deleteProperty 等方法。下面阿寶哥將簡(jiǎn)單介紹 Reflect 對(duì)象所支持的一些靜態(tài)方法。

3.2 Reflect 對(duì)象支持的靜態(tài)方法

Reflect 的所有屬性和方法都是靜態(tài)的,該對(duì)象提供了與 Proxy handler 對(duì)象相關(guān)的 13 個(gè)方法。同樣,這里阿寶哥只列舉以下 5 個(gè)常用的方法:

  • Reflect.get(target, propertyKey[, receiver]):獲取對(duì)象身上某個(gè)屬性的值,類(lèi)似于 target[name]。
  • Reflect.set(target, propertyKey, value[, receiver]):將值賦值給屬性的函數(shù)。返回一個(gè)布爾值,如果更新成功,則返回 true。
  • Reflect.deleteProperty(target, propertyKey):刪除 target 對(duì)象的指定屬性,相當(dāng)于執(zhí)行 delete target[name]。
  • Reflect.has(target, propertyKey):判斷一個(gè)對(duì)象是否存在某個(gè)屬性,和 in 運(yùn)算符的功能完全相同。
  • Reflect.ownKeys(target):返回一個(gè)包含所有自身屬性(不包含繼承屬性)的數(shù)組。

在實(shí)際的 Proxy 使用場(chǎng)景中,我們往往會(huì)結(jié)合 Reflect 對(duì)象提供的靜態(tài)方法來(lái)實(shí)現(xiàn)某些特定的功能。為了讓大家能夠更好地理解并掌握 Proxy 對(duì)象,接下來(lái)的環(huán)節(jié),阿寶哥將列舉 Proxy 對(duì)象的 6 個(gè)使用場(chǎng)景。

四、Proxy 使用場(chǎng)景

這里我們先來(lái)介紹 Proxy 對(duì)象的第一個(gè)使用場(chǎng)景 —— 增強(qiáng)型數(shù)組。

4.1 增強(qiáng)型數(shù)組

定義 enhancedArray 函數(shù)

  1. function enhancedArray(arr) { 
  2.   return new Proxy(arr, { 
  3.     get(target, property, receiver) { 
  4.       const range = getRange(property); 
  5.       const indices = range ? range : getIndices(property); 
  6.       const values = indices.map(function (index) { 
  7.         const key = index < 0 ? String(target.length + index) : index
  8.         return Reflect.get(target, key, receiver); 
  9.       }); 
  10.       return values.length === 1 ? values[0] : values
  11.     }, 
  12.   }); 
  13.  
  14.   function getRange(str) { 
  15.     var [start, end] = str.split(":").map(Number); 
  16.     if (typeof end === "undefined"return false
  17.  
  18.     let range = []; 
  19.     for (let i = start; i < end; i++) { 
  20.       range = range.concat(i); 
  21.     } 
  22.     return range; 
  23.   } 
  24.  
  25.   function getIndices(str) { 
  26.     return str.split(",").map(Number); 
  27.   } 

使用 enhancedArray 函數(shù)

  1. const arr = enhancedArray([1, 2, 3, 4, 5]); 
  2.  
  3. console.log(arr[-1]); //=> 5 
  4. console.log(arr[[2, 4]]); //=> [ 3, 5 ] 
  5. console.log(arr[[2, -2, 1]]); //=> [ 3, 4, 2 ] 
  6. console.log(arr["2:4"]); //=> [ 3, 4] 
  7. console.log(arr["-2:3"]); //=> [ 4, 5, 1, 2, 3 ] 

由以上的輸出結(jié)果可知,增強(qiáng)后的數(shù)組對(duì)象,就可以支持負(fù)數(shù)索引、分片索引等功能。除了可以增強(qiáng)數(shù)組之外,我們也可以使用 Proxy API 來(lái)增強(qiáng)普通對(duì)象。

4.2 增強(qiáng)型對(duì)象

創(chuàng)建 enhancedObject 函數(shù)

  1. const enhancedObject = (target) => 
  2.   new Proxy(target, { 
  3.     get(target, property) { 
  4.       if (property in target) { 
  5.         return target[property]; 
  6.       } else { 
  7.         return searchFor(property, target); 
  8.       } 
  9.     }, 
  10.   }); 
  11.  
  12. let value = null
  13. function searchFor(property, target) { 
  14.   for (const key of Object.keys(target)) { 
  15.     if (typeof target[key] === "object") { 
  16.       searchFor(property, target[key]); 
  17.     } else if (typeof target[property] !== "undefined") { 
  18.       value = target[property]; 
  19.       break; 
  20.     } 
  21.   } 
  22.   return value; 

使用 enhancedObject 函數(shù)

  1. const data = enhancedObject({ 
  2.   user: { 
  3.     name"阿寶哥"
  4.     settings: { 
  5.       theme: "dark"
  6.     }, 
  7.   }, 
  8. }); 
  9.  
  10. console.log(data.user.settings.theme); // dark 
  11. console.log(data.theme); // dark 

以上代碼運(yùn)行后,控制臺(tái)會(huì)輸出以下代碼:

  1. dark 
  2. dark 

通過(guò)觀察以上的輸出結(jié)果可知,使用 enhancedObject 函數(shù)處理過(guò)的對(duì)象,我們就可以方便地訪問(wèn)普通對(duì)象內(nèi)部的深層屬性。

4.3 創(chuàng)建只讀的對(duì)象

創(chuàng)建 Proxy 對(duì)象

  1. const man = { 
  2.   name"semlinker"
  3. }; 
  4.  
  5. const handler = { 
  6.   set"Read-Only"
  7.   defineProperty: "Read-Only"
  8.   deleteProperty: "Read-Only"
  9.   preventExtensions: "Read-Only"
  10.   setPrototypeOf: "Read-Only"
  11. }; 
  12.  
  13. const proxy = new Proxy(man, handler); 

使用 proxy 對(duì)象

  1. console.log(proxy.name); 
  2. proxy.name = "kakuqo"

以上代碼運(yùn)行后,控制臺(tái)會(huì)輸出以下代碼:

  1. semlinker 
  2. proxy.name = "kakuqo"
  3.            ^ 
  4. TypeError: 'Read-Only' returned for property 'set' of object '#<Object>' is not a function 

觀察以上的異常信息可知,導(dǎo)致異常的原因是因?yàn)?handler 對(duì)象的 set 屬性值不是一個(gè)函數(shù)。如果不希望拋出運(yùn)行時(shí)異常,我們可以定義一個(gè) freeze 函數(shù):

  1. function freeze (obj) { 
  2.   return new Proxy(obj, { 
  3.     set () { return true; }, 
  4.     deleteProperty () { return false; }, 
  5.     defineProperty () { return true; }, 
  6.     setPrototypeOf () { return true; } 
  7.   }); 

定義好 freeze 函數(shù),我們使用數(shù)組對(duì)象來(lái)測(cè)試一下它的功能:

  1. let frozen = freeze([1, 2, 3]); 
  2. frozen[0] = 6; 
  3. delete frozen[0]; 
  4. frozen = Object.defineProperty(frozen, 0, { value: 66 }); 
  5. console.log(frozen); // [ 1, 2, 3 ] 

上述代碼成功執(zhí)行后,控制臺(tái)會(huì)輸出 [ 1, 2, 3 ],很明顯經(jīng)過(guò) freeze 函數(shù)處理過(guò)的數(shù)組對(duì)象,已經(jīng)被 “凍結(jié)” 了。

4.4 攔截方法調(diào)用

定義 traceMethodCalls 函數(shù)

  1. function traceMethodCalls(obj) { 
  2.   const handler = { 
  3.     get(target, propKey, receiver) { 
  4.       const origMethod = target[propKey]; // 獲取原始方法 
  5.       return function (...args) { 
  6.         const result = origMethod.apply(this, args); 
  7.         console.log( 
  8.           propKey + JSON.stringify(args) + " -> " + JSON.stringify(result) 
  9.         ); 
  10.         return result; 
  11.       }; 
  12.     }, 
  13.   }; 
  14.   return new Proxy(obj, handler); 

使用 traceMethodCalls 函數(shù)

  1. const obj = { 
  2.   multiply(x, y) { 
  3.     return x * y; 
  4.   }, 
  5. }; 
  6.  
  7. const tracedObj = traceMethodCalls(obj); 
  8. tracedObj.multiply(2, 5); // multiply[2,5] -> 10 

上述代碼成功執(zhí)行后,控制臺(tái)會(huì)輸出 multiply[2,5] -> 10,即我們能夠成功跟蹤 obj對(duì)象中方法的調(diào)用過(guò)程。其實(shí),除了能夠跟蹤方法的調(diào)用,我們也可以跟蹤對(duì)象中屬性的訪問(wèn),具體示例如下:

  1. function tracePropAccess(obj, propKeys) { 
  2.   const propKeySet = new Set(propKeys); 
  3.   return new Proxy(obj, { 
  4.     get(target, propKey, receiver) { 
  5.       if (propKeySet.has(propKey)) { 
  6.         console.log("GET " + propKey); 
  7.       } 
  8.       return Reflect.get(target, propKey, receiver); 
  9.     }, 
  10.     set(target, propKey, value, receiver) { 
  11.       if (propKeySet.has(propKey)) { 
  12.         console.log("SET " + propKey + "=" + value); 
  13.       } 
  14.       return Reflect.set(target, propKey, value, receiver); 
  15.     }, 
  16.   }); 
  17.  
  18. const man = { 
  19.   name"semlinker"
  20. }; 
  21. const tracedMan = tracePropAccess(man, ["name"]); 
  22.  
  23. console.log(tracedMan.name); // GET name; semlinker 
  24. console.log(tracedMan.age); // undefined 
  25. tracedMan.name = "kakuqo"; // SET name=kakuqo 

在以上示例中,我們定義了一個(gè) tracePropAccess 函數(shù),該函數(shù)接收兩個(gè)參數(shù):obj 和 propKeys,它們分別表示需跟蹤的目標(biāo)和需跟蹤的屬性列表。調(diào)用 tracePropAccess 函數(shù)后,會(huì)返回一個(gè)代理對(duì)象,當(dāng)我們?cè)L問(wèn)被跟蹤的屬性時(shí),控制臺(tái)就會(huì)輸出相應(yīng)的訪問(wèn)日志。

4.5 隱藏屬性

創(chuàng)建 hideProperty 函數(shù)

  1. const hideProperty = (target, prefix = "_") => 
  2.   new Proxy(target, { 
  3.     has: (obj, prop) => !prop.startsWith(prefix) && prop in obj, 
  4.     ownKeys: (obj) => 
  5.       Reflect.ownKeys(obj).filter( 
  6.         (prop) => typeof prop !== "string" || !prop.startsWith(prefix) 
  7.       ), 
  8.     get: (obj, prop, rec) => (prop in rec ? obj[prop] : undefined), 
  9.   }); 

使用 hideProperty 函數(shù)

  1. const man = hideProperty({ 
  2.   name"阿寶哥"
  3.   _pwd: "www.semlinker.com"
  4. }); 
  5.  
  6. console.log(man._pwd); // undefined 
  7. console.log('_pwd' in man); // false 
  8. console.log(Object.keys(man)); // [ 'name' ] 

通過(guò)觀察以上的輸出結(jié)果,我們可以知道,利用 Proxy API,我們實(shí)現(xiàn)了指定前綴屬性的隱藏。除了能實(shí)現(xiàn)隱藏屬性之外,利用 Proxy API,我們還可以實(shí)現(xiàn)驗(yàn)證屬性值的功能。

4.6 驗(yàn)證屬性值

創(chuàng)建 validatedUser 函數(shù)

  1. const validatedUser = (target) => 
  2.   new Proxy(target, { 
  3.     set(target, property, value) { 
  4.       switch (property) { 
  5.         case "email"
  6.           const regex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 
  7.           if (!regex.test(value)) { 
  8.             console.error("The user must have a valid email"); 
  9.             return false
  10.           } 
  11.           break; 
  12.         case "age"
  13.           if (value < 20 || value > 80) { 
  14.             console.error("A user's age must be between 20 and 80"); 
  15.             return false
  16.           } 
  17.           break; 
  18.       } 
  19.  
  20.       return Reflect.set(...arguments); 
  21.     }, 
  22.   }); 

使用 validatedUser 函數(shù)

  1. let user = { 
  2.   email: ""
  3.   age: 0, 
  4. }; 
  5.  
  6. user = validatedUser(user); 
  7. user.email = "semlinker.com"; // The user must have a valid email 
  8. user.age = 100; // A user's age must be between 20 and 80 

上述代碼成功執(zhí)行后,控制臺(tái)會(huì)輸出以下結(jié)果:

  1. The user must have a valid email 
  2. user's age must be between 20 and 80 

介紹完 Proxy 對(duì)象的使用場(chǎng)景之后,我們來(lái)繼續(xù)介紹與 Proxy 對(duì)象相關(guān)的一些問(wèn)題。

五、Proxy 相關(guān)問(wèn)題

5.1 this 的指向問(wèn)題

  1. const target = { 
  2.   foo() { 
  3.     return { 
  4.       thisIsTarget: this === target, 
  5.       thisIsProxy: this === proxy, 
  6.     }; 
  7.   }, 
  8. }; 
  9.  
  10. const handler = {}; 
  11. const proxy = new Proxy(target, handler); 
  12. console.log(target.foo()); // { thisIsTarget: true, thisIsProxy: false } 
  13. console.log(proxy.foo()); // { thisIsTarget: false, thisIsProxy: true } 

上述代碼成功執(zhí)行后,控制臺(tái)會(huì)輸出以下結(jié)果:

  1. { thisIsTarget: true, thisIsProxy: false } 
  2. { thisIsTarget: false, thisIsProxy: true } 

通過(guò)以上輸出的結(jié)果,foo 方法中的 this 指向與當(dāng)前的調(diào)用者有關(guān)。看起來(lái)挺簡(jiǎn)單的,但在一些場(chǎng)景下如果稍不注意的話,就會(huì)出現(xiàn)問(wèn)題,比如以下這個(gè)示例:

  1. const _name = new WeakMap(); 
  2.  
  3. class Person { 
  4.   constructor(name) { 
  5.     _name.set(this, name); 
  6.   } 
  7.    
  8.   get name() { 
  9.     return _name.get(this); 
  10.   } 

在以上示例中,我們使用 WeakMap 對(duì)象來(lái)存儲(chǔ) Person 對(duì)象的私有信息。定義完 Person類(lèi),我們就可以通過(guò)以下方式來(lái)使用它:

  1. const man = new Person("阿寶哥"); 
  2. console.log(man.name); // 阿寶哥 
  3.  
  4. const proxy = new Proxy(man, {}); 
  5. console.log(proxy.name); // undefined 

對(duì)于以上的代碼,當(dāng)我們通過(guò) proxy 對(duì)象來(lái)訪問(wèn) name 屬性時(shí),你會(huì)發(fā)現(xiàn)輸出的結(jié)果是undefined。這是因?yàn)楫?dāng)使用 proxy.name 的方式訪問(wèn) name 屬性時(shí),this 指向的是 proxy 對(duì)象,而 _name WeakMap 對(duì)象中存儲(chǔ)的是 man 對(duì)象,所以輸出的結(jié)果是 undefined。

然而,對(duì)于以上的問(wèn)題,如果我們按照以下方式定義 Person 類(lèi),就不會(huì)出現(xiàn)以上問(wèn)題:

  1. class Person { 
  2.   constructor(name) { 
  3.     this._name = name
  4.   } 
  5.   get name() { 
  6.     return this._name; 
  7.   } 
  8.  
  9. const man = new Person("阿寶哥"); 
  10. console.log(man.name); // 阿寶哥 
  11.  
  12. const proxy = new Proxy(man, {}); 
  13. console.log(proxy.name); // 阿寶哥 

另外,如果你對(duì) WeakMap 感興趣的話,可以閱讀 你不知道的 WeakMap 這篇文章。

5.2 get 捕獲器 receiver 參數(shù)是什么

  1. const p = new Proxy(target, { 
  2.   get: function(target, property, receiver) { 
  3.     // receiver 
  4.   } 
  5. }); 

get 捕獲器用于攔截對(duì)象的讀取屬性操作,該捕獲器含有三個(gè)參數(shù):

  • target:目標(biāo)對(duì)象。
  • property:被讀取的屬性名。
  • receiver:指向當(dāng)前的 Proxy 對(duì)象或者繼承于當(dāng)前 Proxy 的對(duì)象。

為了更好地了解 receiver 參數(shù)的描述信息,我們來(lái)舉個(gè)具體的示例:

  1. const proxy = new Proxy({}, 
  2.   { 
  3.     get: function (target, property, receiver) { 
  4.       return receiver; 
  5.     }, 
  6.   } 
  7. ); 
  8.  
  9. console.dir(proxy.getReceiver === proxy); // true 
  10. var inherits = Object.create(proxy); 
  11. console.dir(inherits.getReceiver === inherits); // true 

那么我們能否改變 receiver 指向的對(duì)象呢?答案是可以的,通過(guò) Reflect 對(duì)象提供的get 方法,我們可以動(dòng)態(tài)設(shè)置 receiver 對(duì)象的值,具體使用方式如下所示:

  1. console.dir(Reflect.get(proxy, "getReceiver""阿寶哥")); 

其實(shí) receiver 的名稱(chēng)是來(lái)源于 ECMAScript 規(guī)范:

  • [[Get]] (propertyKey, Receiver) → any Return the value of the property whose key is propertyKey from this object. If any ECMAScript code must be executed to retrieve the property value, Receiver is used as the this value when evaluating the code.
  • [[Set]]  (propertyKey, value, Receiver) → Boolean Set the value of the property whose key is propertyKey to value. If any ECMAScript code must be executed to set the property value, Receiver is used as the this value when evaluating the code. Returns true if the property value was set or false if it could not be set.

以上的 [[Get]] 和 [[Set]] 被稱(chēng)為內(nèi)部方法,ECMAScript 引擎中的每個(gè)對(duì)象都與一組內(nèi)部方法相關(guān)聯(lián),這些內(nèi)部方法定義了其運(yùn)行時(shí)行為。

需要注意的是,這些內(nèi)部方法不是 ECMAScript 語(yǔ)言的一部分。對(duì)于對(duì)象的訪問(wèn)器屬性來(lái)說(shuō),在執(zhí)行內(nèi)部代碼時(shí),Receiver 將被作為 this 的值,同樣使用 Reflect 對(duì)象提供的 API,我們也可以通過(guò)設(shè)置 receiver 參數(shù)的值來(lái)改變 this 的值:

  1. const obj = { 
  2.   get foo() { 
  3.     return this.bar; 
  4.   }, 
  5. }; 
  6.  
  7. console.log(Reflect.get(obj, "foo")); // undefined 
  8. console.log(Reflect.get(obj, "foo", { bar: 2021 })); // 2021 

5.3 包裝內(nèi)置構(gòu)造函數(shù)的實(shí)例

當(dāng)使用 Proxy 包裝內(nèi)置構(gòu)造函數(shù)實(shí)例的時(shí)候,可能會(huì)出現(xiàn)一些問(wèn)題。比如使用 Proxy 代理 Date 構(gòu)造函數(shù)的實(shí)例:

  1. const target = new Date(); 
  2. const handler = {}; 
  3. const proxy = new Proxy(target, handler); 
  4.  
  5. proxy.getDate(); // Error 

當(dāng)以上代碼運(yùn)行后,控制臺(tái)會(huì)輸出以下異常信息:

  1. proxy.getDate(); 
  2.       ^ 
  3. TypeError: this is not a Date object. 

出現(xiàn)以上問(wèn)題的原因是因?yàn)橛行┰鷮?duì)象的內(nèi)部屬性,只有通過(guò)正確的 this 才能拿到,所以 Proxy 無(wú)法代理這些原生對(duì)象的屬性。那么如何解決這個(gè)問(wèn)題呢?要解決這個(gè)問(wèn)題,我們可以為 getDate 方法綁定正確的 this:

  1. const target = new Date(); 
  2. const handler = { 
  3.   get(target, property, receiver) { 
  4.     if (property === "getDate") { 
  5.       return target.getDate.bind(target); 
  6.     } 
  7.     return Reflect.get(target, property, receiver); 
  8.   }, 
  9. }; 
  10.  
  11. const proxy = new Proxy(target, handler); 
  12. console.log(proxy.getDate()); 

5.4 創(chuàng)建可撤銷(xiāo)的代理對(duì)象

通過(guò) Proxy.revocable() 方法可以用來(lái)創(chuàng)建一個(gè)可撤銷(xiāo)的代理對(duì)象,該方法的簽名為:

  1. Proxy.revocable(target, handler); 

相關(guān)的參數(shù)說(shuō)明如下:

  • target:將用 Proxy 封裝的目標(biāo)對(duì)象。可以是任何類(lèi)型的對(duì)象,包括原生數(shù)組,函數(shù),甚至可以是另外一個(gè)代理對(duì)象。
  • handler:一個(gè)對(duì)象,其屬性是一批可選的函數(shù),這些函數(shù)定義了對(duì)應(yīng)的操作被執(zhí)行時(shí)代理的行為。

調(diào)用 Proxy.revocable 方法之后,其返回值是一個(gè)對(duì)象,其結(jié)構(gòu)為:{"proxy": proxy, "revoke": revoke},其中:

  • proxy:表示新生成的代理對(duì)象本身,和用一般方式 new Proxy(target, handler) 創(chuàng)建的代理對(duì)象沒(méi)什么不同,只是它可以被撤銷(xiāo)掉。
  • revoke:撤銷(xiāo)方法,調(diào)用的時(shí)候不需要加任何參數(shù),就可以撤銷(xiāo)掉和它一起生成的那個(gè)代理對(duì)象。

了解完 revocable 方法之后,我們來(lái)舉一個(gè)具體的示例:

  1. const target = {};  
  2. const handler = {}; 
  3. const { proxy, revoke } = Proxy.revocable(target, handler); 
  4.  
  5. proxy.name = "阿寶哥"
  6. console.log(proxy.name); // 阿寶哥 
  7.  
  8. revoke(); 
  9. console.log(proxy.name); // TypeError: Revoked 

當(dāng)以上代碼成功運(yùn)行之后,控制臺(tái)會(huì)輸出以下內(nèi)容:

 

  1. 阿寶哥 
  2. Uncaught TypeError: Cannot perform 'get' on a proxy that has been revoked 
  3.   at <anonymous> 

通過(guò)觀察以上的結(jié)果,我們可知當(dāng) proxy 對(duì)象被撤銷(xiāo)之后,我們就沒(méi)有辦法對(duì)已撤銷(xiāo)的proxy 對(duì)象執(zhí)行任何操作。

六、Proxy 在開(kāi)源項(xiàng)目中的應(yīng)用

因?yàn)?Proxy 對(duì)象能夠提供強(qiáng)大的攔截能力,所以它被應(yīng)用在一些成熟的開(kāi)源項(xiàng)目中,用于實(shí)現(xiàn)響應(yīng)式的功能,比如 vue-next 和 observer-util 項(xiàng)目。對(duì)于 observer-util 這個(gè)項(xiàng)目,阿寶哥已經(jīng)寫(xiě)了一篇 從觀察者模式到響應(yīng)式的設(shè)計(jì)原理 的文章來(lái)介紹該項(xiàng)目,感興趣的小伙伴可以自行閱讀。

而對(duì)于 vue-next 項(xiàng)目來(lái)說(shuō),響應(yīng)式的功能被封裝在 @vue/reactivity 模塊中,該模塊為我們提供了一個(gè) reactive 函數(shù)來(lái)創(chuàng)建響應(yīng)式對(duì)象。下面我們來(lái)簡(jiǎn)單了解一下 reactive函數(shù)的實(shí)現(xiàn):

  1. // packages/reactivity/src/reactive.ts 
  2. export function reactive(target: object) { 
  3.   if (target && (target as Target)[ReactiveFlags.IS_READONLY]) { 
  4.     return target 
  5.   } 
  6.   return createReactiveObject( 
  7.     target, 
  8.     false
  9.     mutableHandlers, 
  10.     mutableCollectionHandlers 
  11.   ) 

在 reactive 函數(shù)內(nèi)部,會(huì)繼續(xù)調(diào)用 createReactiveObject 函數(shù)來(lái)創(chuàng)建響應(yīng)式對(duì)象,該函數(shù)也是被定義在 reactive.ts 文件中,該函數(shù)的的具體實(shí)現(xiàn)如下:

  1. // packages/reactivity/src/reactive.ts 
  2. function createReactiveObject( 
  3.   target: Target, 
  4.   isReadonly: boolean, 
  5.   baseHandlers: ProxyHandler<any>, 
  6.   collectionHandlers: ProxyHandler<any
  7. ) { 
  8.   // 省略部分代碼   
  9.   const proxyMap = isReadonly ? readonlyMap : reactiveMap 
  10.   const existingProxy = proxyMap.get(target) 
  11.   if (existingProxy) { 
  12.     return existingProxy 
  13.   } 
  14.   const proxy = new Proxy( 
  15.     target, 
  16.     targetType === TargetType.COLLECTION ? collectionHandlers : baseHandlers 
  17.   ) 
  18.   proxyMap.set(target, proxy) 
  19.   return proxy 

在 createReactiveObject 函數(shù)內(nèi)部,我們終于見(jiàn)到了期待已久的 Proxy 對(duì)象。當(dāng) target 對(duì)象不是集合類(lèi)型的對(duì)象,比如 Map、Set、WeakMap 和 WeakSet 時(shí),在創(chuàng)建 Proxy 對(duì)象時(shí),使用的是 baseHandlers,該 handler 對(duì)象定義了以下 5 種捕獲器:

  1. export const mutableHandlers: ProxyHandler<object> = { 
  2.   get, 
  3.   set
  4.   deleteProperty, 
  5.   has, 
  6.   ownKeys 

其中 get 和 set 捕獲器是分別用于收集 effect 函數(shù)和觸發(fā) effect 函數(shù)的執(zhí)行。好了,這里阿寶哥只是介紹一下 @vue/reactivity 中的 reactive 函數(shù),關(guān)于該模塊是如何實(shí)現(xiàn)響應(yīng)式的細(xì)節(jié),這里就不展開(kāi)介紹了,阿寶哥后續(xù)會(huì)單獨(dú)寫(xiě)一篇文章來(lái)詳細(xì)分析該模塊的功能。

七、參考資源MDN - Proxy

MDN - Reflect

exploringjs - proxies

stackoverflow - what-is-a-receiver-in-javascript

 

責(zé)任編輯:武曉燕 來(lái)源: 全棧修仙之路
相關(guān)推薦

2022-11-04 08:19:18

gRPC框架項(xiàng)目

2020-06-12 09:20:33

前端Blob字符串

2020-07-28 08:26:34

WebSocket瀏覽器

2019-06-26 09:00:53

Windows 10Windows便箋

2011-09-15 17:10:41

2009-12-10 09:37:43

2022-10-13 11:48:37

Web共享機(jī)制操作系統(tǒng)

2010-08-23 09:56:09

Java性能監(jiān)控

2022-06-30 08:01:33

ProxyReflecthandler

2020-09-15 08:35:57

TypeScript JavaScript類(lèi)型

2021-10-17 13:10:56

函數(shù)TypeScript泛型

2021-12-22 09:08:39

JSON.stringJavaScript字符串

2012-11-23 10:57:44

Shell

2020-08-11 11:20:49

Linux命令使用技巧

2021-12-29 11:38:59

JS前端沙箱

2015-06-19 13:54:49

2014-03-12 09:23:06

DevOps團(tuán)隊(duì)合作

2012-06-26 15:49:05

2015-08-13 09:03:14

調(diào)試技巧

2023-12-21 14:40:09

Python編程語(yǔ)言
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

日本精品视频| caoporn国产精品免费视频| 亚洲国产日本| 亚洲欧美日韩另类| 99久久99精品| 中文在线中文资源| 亚洲卡通欧美制服中文| 精品亚洲欧美日韩| 国产精品午夜福利| 性伦欧美刺激片在线观看| 中文字幕欧美日韩在线| 成人在线电影网站| 亚洲人成777| 第一福利永久视频精品| 国产对白在线播放| 精品亚洲综合| 成人一道本在线| 国产精品久久久久久久av电影| 欧美黑吊大战白妞| 欧美一级精品| 日韩精品小视频| 樱花草www在线| 在线观看精品| 激情成人中文字幕| 久久观看最新视频| av在线播放网站| 91啪亚洲精品| 国产亚洲一区二区三区在线播放| 一级特黄色大片| 久久久久久网| 91成人性视频| 日本少妇xxxx动漫| 欧美日韩国产成人精品| 色七七影院综合| 四虎国产精品成人免费入口| 欧洲亚洲视频| 精品88久久久久88久久久| 91精产国品一二三产区别沈先生| 欧美va在线观看| 欧美日韩裸体免费视频| 男女激情免费视频| 久久青青色综合| 亚洲人成影院在线观看| 夜夜春亚洲嫩草影视日日摸夜夜添夜| 欧美zzoo| 久久久综合视频| 久久久久久久久久久久久9999| www.99视频| 国产精品一品二品| 成人免费看片视频| 国产女人18毛片水18精| 国产中文一区二区三区| 91精品久久久久| 中文字幕在线观看视频一区| 日本美女视频一区二区| 国产精品久久久久久av福利软件| 在线观看国产区| 日韩av不卡在线观看| 国产精品福利在线观看网址| 蜜臀尤物一区二区三区直播| 日韩国产一区二| 国产精品第2页| 在线观看免费视频一区| 捆绑调教美女网站视频一区| 国产精品尤物福利片在线观看| 亚洲一区中文字幕永久在线| 精品一区免费av| 亚洲最大的av网站| 国产91免费看| 91污片在线观看| 日产精品久久久一区二区| av资源在线观看免费高清| 中文字幕一区二区三区蜜月| 18视频在线观看娇喘| 国产99re66在线视频| 精品久久久中文| 亚洲少妇第一页| 亚洲爽爆av| 亚洲精品在线观看网站| 中文人妻一区二区三区| 日韩伦理视频| 欧美黑人性生活视频| 999这里只有精品| 免费xxxx性欧美18vr| 亚洲影影院av| 天天综合网在线观看| 日本一区二区三区四区| 裸体裸乳免费看| 国产在线看片免费视频在线观看| 日本丶国产丶欧美色综合| 伊人网在线综合| 9l视频自拍九色9l视频成人| 亚洲人成免费电影| 男女性高潮免费网站| 亚洲区国产区| 国产精品视频在线观看| 亚洲国产精品视频在线| 国产婷婷一区二区| 成人高清dvd| 欧美色999| 欧美刺激午夜性久久久久久久| 久久精品老司机| 中文av一区| 日本高清不卡的在线| aaa一区二区| 久久综合狠狠综合久久激情| av中文字幕av| 国产一区高清| 亚洲欧美精品在线| 久久人人爽人人爽人人| 日韩成人av影视| 国产在线精品一区二区三区| 日本暖暖在线视频| 欧美日韩精品在线视频| 中文字幕一二三区| 第四色成人网| 热久久99这里有精品| www.麻豆av| 国产精品不卡视频| 亚洲国产精品毛片av不卡在线| 视频精品一区| 日韩在线观看免费全| 无码人妻丰满熟妇区bbbbxxxx| 国产成a人亚洲精| 在线观看日韩羞羞视频| 欧美日韩免费看片| 日韩成人av在线| 久久免费在线观看视频| 国产在线不卡一区| 亚洲一区二区三区涩| 中文字幕这里只有精品| 亚洲国产成人精品女人久久久| 欧美三级黄色大片| 久久精品国产色蜜蜜麻豆| 麻豆av福利av久久av| mm视频在线视频| 精品国产一区二区国模嫣然| 男人与禽猛交狂配| 狠狠色狠狠色综合日日91app| 午夜视频久久久| 日日夜夜天天综合| 伊人久久免费视频| 中文字幕免费高清在线观看| 国产丝袜在线精品| 老头吃奶性行交视频| 神马影视一区二区| 日本sm极度另类视频| 色资源在线观看| 欧美日韩亚洲精品内裤| 久久丫精品国产亚洲av不卡| 亚洲欧美日韩视频二区| 欧美韩国日本精品一区二区三区| caoporn视频在线| 日韩精品福利在线| 国产精品21p| 国产亚洲一区二区在线观看| 国产免费视频传媒| 日韩精品电影| 亚洲qvod图片区电影| 色呦呦呦在线观看| 亚洲激情小视频| 久久久久久久久久久影院| 久久久久国色av免费看影院| 国产精品wwwww| 欧美亚洲精品在线| 成人信息集中地欧美| 欧洲性视频在线播放| 亚洲国产日韩欧美综合久久| 你懂的国产视频| 国产亚洲精品福利| 国产精品嫩草影院8vv8| 欧美激情 亚洲a∨综合| 精品蜜桃一区二区三区| 3d欧美精品动漫xxxx无尽| 久久精品电影一区二区| 亚洲春色一区二区三区| 欧美丝袜一区二区| 国产一级淫片久久久片a级| 国产麻豆9l精品三级站| 亚洲 高清 成人 动漫| 日韩理论在线| 国产欧美亚洲日本| 亚洲日本在线观看视频| 欧美成人免费全部| 你懂的在线视频| 9191成人精品久久| 国产香蕉视频在线| 国产精品不卡一区| 日本丰满少妇裸体自慰 | 久久精品99久久香蕉国产色戒| 91极品身材尤物theporn| 亚洲国产一区在线观看| 久久午夜福利电影| 国产一区二区不卡| av动漫免费看| 欧美成熟视频| 日本亚洲导航| 国产精品视频3p| 国产美女久久精品| 三妻四妾的电影电视剧在线观看 | 久久艹中文字幕| **国产精品| 日韩av片电影专区| 91丝袜在线| 精品国内自产拍在线观看| 你懂的视频在线观看| 欧美mv日韩mv| 一二三四区在线| 日韩欧美主播在线| 日本中文字幕网| 亚洲精品中文字幕在线观看| 国产高潮呻吟久久| 99久久久免费精品国产一区二区| 色播五月激情五月| 久久国产直播| 欧美成人高潮一二区在线看| 99久久婷婷这里只有精品| 欧美日韩在线一二三| 久久亚州av| 高清av免费一区中文字幕| 欧美高清影院| 国产精品成久久久久三级 | 国产九九精品视频| 樱桃视频成人在线观看| 欧美精品18videos性欧| 含羞草www国产在线视频| 中文字幕av一区| 国产中文在线视频| 亚洲欧美日韩国产成人| 天堂网av在线播放| 亚洲成人黄色在线| 日本韩国免费观看| 精品国产91洋老外米糕| 亚洲第一黄色片| 精品久久人人做人人爱| 精品人妻一区二区三区三区四区| 4438x亚洲最大成人网| 一级特黄特色的免费大片视频| 欧美三级欧美一级| 伊人网中文字幕| 欧美日韩国产美女| 亚洲专区在线播放| 欧美日韩1区2区| 一区二区不卡视频在线观看| 欧美日韩一区二区三区视频| 精品一区二三区| 欧美午夜一区二区三区| 中文字幕 亚洲视频| 欧美日韩一区在线观看| 一级特黄aaa大片| 日韩一区二区在线看| www.香蕉视频| 亚洲国产精品美女| 四虎精品在线| 日韩精品免费综合视频在线播放 | 日韩中文字幕视频在线观看| 在线视频1区2区| 久久中文字幕在线| 俺来也官网欧美久久精品| 性色av一区二区三区在线观看| 岛国av免费在线观看| 欧美一级在线播放| 在线日本欧美| 亚洲一区二区日本| 精品亚洲自拍| 奇米视频888战线精品播放| 日韩精品看片| 穿情趣内衣被c到高潮视频| 影音先锋一区| 成年人免费在线播放| 蜜臀国产一区二区三区在线播放| 亚洲综合伊人久久| 成人免费高清在线| 一级肉体全黄裸片| 亚洲蜜臀av乱码久久精品 | 国产日韩1区| 男女男精品视频站| 国产成人精品一区二| 美国黄色一级毛片| 中文字幕av一区二区三区高 | 亚洲黄色免费| 无码少妇一区二区三区芒果| 国产真实精品久久二三区| 午夜不卡久久精品无码免费| 国产午夜精品久久久久久免费视 | 国产三级黄色片| 亚洲黄色免费网站| 亚洲国产精品无码久久久| 在线成人午夜影院| 三级无遮挡在线观看| 精品国产依人香蕉在线精品| 欧洲一区精品| 成人黄色在线免费| 亚洲午夜久久| 六月婷婷激情综合| 欧美a一区二区| 黄色性视频网站| 亚洲欧美自拍偷拍色图| 成年人视频在线免费看| 91精品免费在线观看| 女人偷人在线视频| 欧美精品video| 91亚洲精品在看在线观看高清| 国产三区精品| 亚洲情侣在线| www.99在线| 91麻豆免费观看| 免费在线观看亚洲| 欧美日韩激情一区二区三区| 蜜臀久久久久久999| 中文字幕久热精品视频在线| 人成在线免费网站| 超碰97在线资源| 91精品国产调教在线观看| 日韩中文字幕免费在线| www.欧美.com| 欧美精品一区二区蜜桃| 欧美日韩国产综合一区二区三区 | 5278欧美一区二区三区| 日韩综合一区二区三区| 亚洲午夜精品久久久久久浪潮| 欧美亚洲网站| 日本黄色录像片| 亚洲午夜在线视频| 精品国产无码一区二区三区| 色婷婷综合成人| 国产亚洲精品精品国产亚洲综合| 欧美视频小说| 久久久久久久波多野高潮日日| 午夜视频在线观看国产| 亚洲一区视频在线| 草逼视频免费看| 欧美日韩高清在线观看| 精品国产亚洲日本| 致1999电视剧免费观看策驰影院| 日韩成人一区二区| 午夜精产品一区二区在线观看的| 欧美性猛交xxxx久久久| 亚洲欧美日韩精品永久在线| 午夜精品99久久免费| 欧美国产不卡| 精品国产一区三区| 99精品视频一区| 毛片视频网站在线观看| 亚洲精品视频免费| 625成人欧美午夜电影| 欧美日韩系列| 久久青草久久| 亚洲天堂av中文字幕| 欧美日韩mp4| 在线播放免费av| 国产精品美女久久久久av福利| 亚洲东热激情| 久久人人爽人人人人片| 欧美日韩亚洲系列| 国产露出视频在线观看| 国产欧美精品在线| 综合久久精品| 一级黄色片毛片| 欧美日韩中文字幕在线视频| 精华区一区二区三区| 国产精品视频免费在线观看| 午夜精品视频一区二区三区在线看| 超碰在线资源站| 亚洲国产精品久久久久秋霞影院 | 九九综合久久| 久久久久久蜜桃一区二区| 亚洲视频中文字幕| 亚洲精品国产精品国| 欧美亚洲成人免费| 日韩精品水蜜桃| 精品伦一区二区三区| 懂色aⅴ精品一区二区三区蜜月| 国产精品一区二区三区四区色| 国产精品一区二区性色av| 欧美国产先锋| 亚洲熟妇无码av| 3atv在线一区二区三区| 18aaaa精品欧美大片h| 日韩欧美精品在线不卡| 国产在线精品一区二区| 国产精品久久久久久久久久久久久久久久久 | 99re视频在线播放| 久久久久综合| 久久精品黄色片| 亚洲男人的天堂在线播放| 亚洲伊人精品酒店| 欧美亚洲另类色图| 亚洲情趣在线观看| 日韩精品123| 亚洲自拍中文字幕| 久久中文字幕一区二区三区| 欧美三级在线免费观看| 亚洲欧美中文另类| 91蝌蚪精品视频| 超碰av在线免费观看| 亚洲大片精品永久免费| 天堂中文а√在线| 久久久福利视频| 国产电影一区二区三区|