JavaScript - 封裝



什麼是封裝?

JavaScript 中的封裝是一種透過將相關的屬性和方法捆綁在一個名稱空間下(例如函式、類或物件)來保持它們相關的方式。在 JavaScript 中,封裝可以透過閉包、類以及 getter 和 setter 來實現。

封裝是面向物件程式語言(例如 JavaScript)中的一個基本概念,與繼承和多型並列。

它用於隱藏資料,只向外界提供訪問所需資料的能力,從而提高資料完整性和安全性。

為什麼需要封裝?

讓我們透過以下示例討論 JavaScript 中封裝的必要性。

例如,您在程式碼中定義了以下物件。

const car = {
   Brand: "Honda city",
   model: "sx",
   year: 2016,
}

任何人都可以訪問 car 物件的屬性,如下所示。

car.Brand

同樣,任何人都可以更改 car 物件的任何屬性的值,如下所示。

car.Brand = true;

這裡,Brand 屬性的值從字串更改為布林值。因此,需要保護物件的原始資料,並向外界提供有限的資料訪問許可權。

在這種情況下,封裝的概念就出現了。

在 JavaScript 中實現封裝的不同方法

有三種不同的方法可以實現封裝。

  • 使用函式閉包

  • 使用 ES6 類

  • 使用 Getter 和 Setter

在這裡,我們將逐一學習每種實現封裝的方法。

使用函式閉包實現封裝

JavaScript 函式閉包是一個概念,它允許內部函式即使在外部函式執行完畢後也能訪問外部函式中定義的變數。外部函式中定義的變數無法在其函式作用域之外訪問,但可以透過內部作用域訪問。

示例

在下面的程式碼中,shoppingCart() 函式是一個外部函式,它包含變數和函式。外部函式有自己的私有作用域。

carItems[] 陣列用於儲存購物車中的商品。

add() 函式可以訪問 carItems[] 陣列並新增商品。

remove() 函式檢查 carItems[] 是否包含您需要移除的商品。如果是,則移除該商品。否則,它會列印一條訊息,表明您無法移除該商品。

shoppingCart() 函式返回一個包含 add() 和 remove() 函式的物件。

建立 shoppingCart() 函式的新例項後,您可以使用 add() 和 remove() 函式來操作購物車資料。

<html>
<body>
  <p id = "output"> </p>
  <script>
    let output = document.getElementById("output");
    function shoppingCart() {
      const cartItems = [];
      function add(item) {
        cartItems.push(item);
        output.innerHTML += `${item.name} added to the cart. <br>`;
      }
      function remove(itemName) {
        const index = cartItems.findIndex(item => item.name === itemName);
        if (index !== -1) {
          const removedItem = cartItems.splice(index, 1)[0];
          output.innerHTML += `${removedItem.name} removed from the cart. <br>`;
        } else {
          output.innerHTML += `Item ${itemName} not found in the cart. <br>`;
        }
      }
      return {
        add,
        remove,
      };
    }

    // Defining items
    const item1 = { name: 'Car', price: 1000000 };
    const item2 = { name: 'Bike', price: 100000 };
    // Create a new Shopping cart
    const cart = shoppingCart();
    // Adding items to the cart
    cart.add(item1);
    cart.add(item2);
    // Remove bike from the cart
    cart.remove('Bike');
  </script>
</body>
</html>

輸出

Car added to the cart.
Bike added to the cart.
Bike removed from the cart.

這樣,任何人都無法直接訪問和修改 carItems[] 陣列。

使用 ES6 類和私有變數實現封裝

在 JavaScript 中,您可以使用類和私有變數來實現封裝。

JavaScript 中的私有變數(欄位)

要定義私有類變數,可以在變數名稱後面加上“#”符號。例如,在下面的程式碼中,“name”是私有變數。

class car {
    #name= "TATA";
}

如果你嘗試透過類的例項訪問名稱,它會給你一個錯誤,提示私有欄位無法在類外部訪問。

為了實現封裝,你可以在類中定義私有變數,並使用不同的方法讓它們訪問外部世界。

示例

在下面的示例中,我們定義了汽車類。

汽車類包含“品牌”、“名稱”和“里程”三個私有變數。

getMilage() 方法用於返回汽車的里程,而 setMilage() 方法用於設定汽車的里程。

我們建立了汽車類的物件,並使用該方法訪問和修改私有欄位。如果你嘗試訪問類的私有欄位,程式碼會丟擲一個錯誤。

你也可以在類中定義更多方法來訪問和修改其他私有欄位。

<html>
<body>
  <div id = "output1">The car mileage is:  </div>
  <div id = "output2">After updating the car mileage is:  </div>
  <script>
    class Car {
      #brand = "TATA"; // Private field
      #name = "Nexon"; // Private field
      #milage = 16;    // Private field

      getMilage() {
        return this.#milage; // Accessing private field
      }

    setMilage(milage) {
      this.#milage = milage; // Modifying private field
    }
    }

    let carobj = new Car();
    document.getElementById("output1").innerHTML += carobj.getMilage();
    carobj.setMilage(20);
    document.getElementById("output2").innerHTML += carobj.getMilage();
    // carobj.#milage);  will throw an error.
  </script>
</body>
</html>

輸出

The car mileage is: 16
After updating the car mileage is: 20

使用 Getter 和 Setter 實現封裝

JavaScript 的 getter 和 setter 可以分別使用 get 和 set 關鍵字定義。getter 用於獲取類屬性,setter 用於更新類屬性。

它們與類方法非常相似,但使用 get/set 關鍵字後跟方法名進行定義。

示例

在下面的示例中,我們定義了 User 類,其中包含三個名為 username、password 和 isLoggedIn 的私有欄位。

定義了名為 username 的 getter 和 setter 來獲取和設定使用者名稱。在這裡,你可以觀察到 getter 和 setter 方法的名稱相同。

之後,我們建立了類的物件,並使用 getter 和 setter 作為屬性來訪問和更新類的 username 欄位。

你也可以為其他類欄位建立 getter 和 setter。

<html>
<body>
    <div id = "output1">The initial username is:  </div>
    <div id = "output2">The new username is:  </div>
    <script>
        class User {
            #username = "Bob";
            #password = "12345678";
            #isLoggedIn = false;
            get username() {
                return this.#username;
            }

            set username(user) {
                this.#username = user;
            }
        }

        const user = new User();
        document.getElementById("output1").innerHTML += user.username;
        user.username = "Alice";
        document.getElementById("output2").innerHTML += user.username;
    </script>
</body>
</html>

輸出

The initial username is: Bob
The new username is: Alice

從以上所有內容,你可以理解封裝就是將變數設為私有,並限制其對外部世界的訪問。

JavaScript 中封裝的優勢

這裡,我們列出了 JavaScript 中封裝的一些好處:

  • 資料保護 - 封裝允許你透過將類資料設為私有來控制對它的訪問。你只能公開必要的資料和方法。因此,沒有人會錯誤地修改資料。此外,你可以在更新資料時驗證它們。如果新資料無效,你可以丟擲一個錯誤。

  • 程式碼可重用性 - 類是物件的模板,你可以重用它來建立具有不同資料的物件。

  • 程式碼維護 - 封裝使程式碼易於維護,因為每個物件都是獨立的,如果你對一個物件進行更改,它不會影響其他程式碼。

廣告