JavaScript - 代理 (dài lǐ)



JavaScript 中的代理 (dài lǐ)

JavaScript 的代理是允許你包裝特定物件並自定義物件基本操作(例如獲取和設定物件屬性)的物件。簡而言之,使用代理物件,你可以為物件新增自定義行為。代理用於實現諸如日誌記錄、快取和安全之類的功能。

我們可以使用 JavaScript 中的 Proxy() 建構函式建立一個代理物件。建構函式接受兩個引數——目標物件和處理程式物件。它返回目標物件的新代理物件。

語法 (yǔ fǎ)

以下是 JavaScript 中建立代理物件的語法:

const obj = new Proxy(targetObj, Handler);

我們在上面的語法中使用了帶有 new 關鍵字的 Proxy() 建構函式。

引數 (cān shù)

Proxy 建構函式接受兩個引數。

  • targetObj − 這是你想要為其建立代理物件並自定義其預設行為的目標物件。

  • Handler − 這是一個包含自定義目標物件行為的功能的物件。

示例 (shì lì)

在下面的示例中,person 物件包含 name 和 age 屬性。

我們為名為 proxyObj 的 person 物件定義了代理物件。此外,我們將 handler 物件作為 Proxy() 建構函式引數傳遞。

在 handler 物件中,我們定義了 getter 來訪問物件屬性。getter 檢查物件是否包含你正在查詢的屬性。如果是,則返回屬性值。否則,它返回一條訊息,說明物件不包含該屬性。

<html>
<body>
   <div id = "demo1">The name of the person is: </div>
   <div id = "demo2">The height of the person is: </div>
   <script>
      const person = {
         name: "Same",
         age: 32,
      }
      const handler = {
         // Defining the getters
         get: function (object, property) {
            return object[property] ? object[property] : 
            'Object doesnt contain the property.';
         }
      }
      const proxyObj = new Proxy(person, handler);
      document.getElementById("demo1").innerHTML += proxyObj.name;
      document.getElementById("demo2").innerHTML += proxyObj.height;
   </script>
</body>
</html>

輸出 (shū chū)

The name of the person is: Same
The height of the person is: Object doesnt contain the property.

當您從物件訪問不存在的屬性時,它將返回 undefined。在這裡,我們自定義了物件的預設行為以返回一種友好易讀的方式。

如果在建立代理物件時傳遞空 handler 物件,則代理物件的工作方式與原始物件相同。

JavaScript 代理處理程式 (dài lǐ chǔ lǐ chéng xù)

JavaScript 中有多個代理處理程式可用,我們下面介紹了其中一些。代理處理程式用於覆蓋物件的預設行為。

JavaScript get() 代理處理程式 (dài lǐ chǔ lǐ chéng xù)

JavaScript 中的 get() 代理處理程式允許你更改物件的屬性訪問行為。

語法 (yǔ fǎ)

請按照以下語法使用帶有代理物件的 get() 代理處理程式。

get(target, property, receiver)

引數 (cān shù)

  • target − 這是目標物件。

  • property − 這是需要訪問其值的屬性。

  • receiver − 它本身就是一個代理物件。

示例 (shì lì)

在下面的程式碼中,watch 物件包含 brand、color 和 price 屬性。我們已經為 watch 物件建立了代理。

handler 物件包含 get() 處理程式,如果屬性存在則返回屬性值。否則,它返回一條易讀的訊息。

<html>
<body>
   <div id = "output1">Brand:  </div>
   <div id = "output2">Price:  </div>
   <script>
      const watch = {
         brand: "Casio",
         color: "Blue",
         price: null,
      }

      const handler = {
         get(object, property) {
            return object[property] != null ? object[property] : "Property is null.";
         }
      }
      const wathcProxy = new Proxy(watch, handler);
      document.getElementById("output1").innerHTML += wathcProxy.brand;
      document.getElementById("output2").innerHTML += wathcProxy.price;
   </script>
</body>
</html>

輸出 (shū chū)

Brand: Casio
Price: Property is null.

JavaScript set() 代理處理程式 (dài lǐ chǔ lǐ chéng xù)

JavaScript 中的 set() 代理處理程式用於更改更新物件屬性的預設行為。

語法 (yǔ fǎ)

請按照以下語法使用 set() 代理處理程式。

set(target, property, value, receiver)

引數 (cān shù)

  • target − 這是目標物件。

  • property − 這是要更改其值的屬性。

  • value − 這是更新後的值。

  • receiver − 它本身就是一個代理物件。

示例 (shì lì)

在下面的程式碼中,handler 物件包含 set() 代理處理程式。set() 處理程式檢查屬性是否等於“price”。如果是,則使用新值更新屬性值。否則,它將“不可用”設定為物件屬性。

<html>
<body>
   <p id = "demo"> </p>
   <script>
      const output = document.getElementById("demo");
      const watch = {
         brand: "Casio",
         color: "Blue",
         price: null,
      }
      const handler = {
         set(object, property, value) {
            if (property === "price") {
               object[property] = value;
            }
            else {
               object[property] = "Not Available";
            }
         }
      }
      const wathcProxy = new Proxy(watch, handler);
      wathcProxy.price = 2000;
      wathcProxy.dial = "Round";
      output.innerHTML += "Price: " + wathcProxy.price + "<br>";
      output.innerHTML += "Dial: " + wathcProxy.dial + "<br>";
   </script>
</body>
</html>

輸出 (shū chū)

Price: 2000
Dial: Not Available

JavaScript apply() 代理處理程式 (dài lǐ chǔ lǐ chéng xù)

apply() 代理處理程式用於更改函式呼叫的預設行為。

語法 (yǔ fǎ)

請按照以下語法使用 apply() 代理處理程式。

apply(target, thisArg, arguments)

引數 (cān shù)

  • target − 這是需要執行的目標函式。

  • thisArg − 它指的是上下文,其元素應在函式體中使用 this 關鍵字訪問。

  • arguments − 這是要傳遞給函式的引數陣列。

示例 (shì lì)

在下面的程式碼中,getDetails 是為 getWatchDetails() 函式建立的代理物件。handler 物件包含 apply() 方法並呼叫目標函式。

我們透過傳遞 watch 物件作為引數來呼叫 getDetails() 代理。

<html>
<body>
   <p id = "output"> </p>
   <script>
      const watch = {
         brand: "Casio",
         color: "Blue",
         price: 2000,
      }
      const getWatchDetails = function (watch) {
         return `Brand: ${watch.brand},  
         Color: ${watch.color}, 
         price: ${watch.price}`;
      }
      const getDetails = new Proxy(getWatchDetails, {
         apply(target, thisArg, args) {
            return target(...args).toUpperCase();
         }
      });
      document.getElementById("output").innerHTML += getDetails(watch);
   </script>
</body>
</html>

輸出 (shū chū)

BRAND: CASIO, COLOR: BLUE, PRICE: 2000

JavaScript 中代理物件的用途 (yòng tú)

本文將結合示例講解使用代理物件的好處。

用於驗證

你可以在 JavaScript 中使用代理物件來驗證屬性值,同時更新屬性值或向物件新增新屬性。

示例 (shì lì)

在下面的程式碼中,numbers 物件包含 num1、num2 和 num3 屬性。set() 代理處理器檢查新值是否大於當前值。如果是,則更新值;否則,保留舊值。

<html>
<body>
   <p id = "demo"> </p>
   <script>
      const output = document.getElementById("demo");
      const numbers = {
         num1: 10,
         num2: 20,
         num3: 30,
      }
      const handler = {
         set(object, property, value) {
            if (value > object[property]) { 
               // Validing the new value using the previous value
               object[property] = value;
            }
         }
      }
      const numberProxy = new Proxy(numbers, handler);
      numberProxy.num1 = 20;
      numberProxy.num2 = 10;

      output.innerHTML += "num1: " + numbers.num1 + ", num2: " + numbers.num2;
   </script>
</body>
</html>

輸出 (shū chū)

num1: 20, num2: 20

用於訪問控制

你也可以使用代理處理器來控制 JavaScript 中物件的訪問。例如,你可以限制使用者更新物件屬性,使其成為只讀。

示例 (shì lì)

在下面的程式碼中,每當你嘗試更新物件屬性值時,它都會列印一條訊息,指出該物件是隻讀的。

<html>
<body>
   <p id = "demo"> </p>
   <script>
      const output = document.getElementById("demo");
      const numbers = {
         num1: 10,
         num2: 20,
         num3: 30,
      }
      const handler = {
         set(object, property, value) {
            output.innerHTML += "Object is read-only.<br>"
         }
      }
      const numberProxy = new Proxy(numbers, handler);
      numberProxy.num1 = 20;
      output.innerHTML += "num1: " + numberProxy.num1;
   </script>
</body>
</html>

輸出 (shū chū)

Object is read-only.
num1: 10

副作用

當任何人嘗試訪問或更新物件屬性時,你可能會呼叫函式或類方法。

示例 (shì lì)

在下面的示例中,emailValidator() 函式檢查電子郵件是否包含“@”。如果是,則返回 true;否則,返回 false。

在 set() 代理處理器中,我們根據 emailValidator() 函式的返回值來更新屬性值。

<html>
<body>
   <p id = "output"> </p>
   <script>
      const emails = {
         email1: "abcd@gmail.com",
      }
      // Function to validate the email
      function emailValidator(email) {
         if (email.includes("@")) {
            return true;
         } else {
            return false;
         }
      }
      const handler = {
         set(object, property, value) {
            if (emailValidator(value)) {
               object[property] = value;
            }
         }
      }
      const emailProxy = new Proxy(emails, handler);
      emailProxy.email1 = "nmc@gmail.com";
      document.getElementById("output").innerHTML = 
      "email1: " + emailProxy.email1;
   </script>
</body>
</html>

輸出 (shū chū)

email1: nmc@gmail.com

然而,代理處理器的用法並不侷限於此,我們無法在本教程中涵蓋每種用例。因此,你可以探索更多代理處理器的用例。

JavaScript 代理處理器列表

這裡列出了 JavaScript 中的所有代理處理器。

代理處理器方法

序號 代理處理器 描述
1 apply() 它改變函式呼叫的預設行為。
2 construct() 攔截新物件的構造。
3 defineProperty() 改變定義新屬性的行為。
4 deleteProperty() 改變刪除新屬性的行為。
5 get() 改變訪問物件屬性的行為。
6 getOwnPropertyDescriptor() 攔截物件的 getOwnPropertyDescriptor() 方法。
7 getPrototypeOf() 攔截內部方法。
8 has() 操縱檢查物件是否包含該屬性。
9 isExtensible() 攔截物件的 isExtensible() 方法。
10 ownKeys() 改變 ownKeys() 方法的行為。
11 preventExtensions() 攔截阻止物件擴充套件的操作。
12 set() 改變新增新屬性或更新物件屬性值的預設行為。
13 setPrototypeOf() 自定義 Object.setPrototypeOf() 方法。

建構函式

序號 建構函式 描述
1 Proxy() 用於建立代理物件。

靜態方法

序號 方法 描述
1 revocable() 它也用於建立新的代理物件,類似於 Proxy() 建構函式。
廣告