JavaScript - Encapsulation(封装)



什么是封装(Encapsulation)?

JavaScript 中的封装(Encapsulation)是一种通过将相关属性和方法捆绑在一起来将它们保存在单个命名空间下的方法。它可以是一个函数、一个类或一个对象。在 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

在这里,我们将逐一学习实现封装的每种方法。

函数闭包 (Function Closure) 实现封装

JavaScript 函数闭包是一个概念,允许内部函数访问外部函数中定义的变量,即使在执行外部函数之后也是如此。外部函数中定义的变量不能在其功能范围之外访问,但可以使用内部范围访问。

在下面的代码中,shoppingCart() 函数是一个包含变量和函数的外部函数。外部函数有其 private 范围。

carItems[] 数组用于存储购物车的商品。

add() 函数可以访问 carItems[] 数组并添加项目。

remove() 函数检查 cartItems[] 是否包含您需要删除的项目。如果是,则会删除该项目。否则,它将打印您无法删除该项目的消息。

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.

这样,没有人可以直接访问和修改 cartItems[] 数组。

使用 ES6 类和私有变量实现封装

在 JavaScript 中,你可以使用类和私有变量来实现封装。

JavaScript 中的私有变量(字段)

要定义私有类变量,您可以编写变量名称,后跟 '#' 符号。例如,在下面的代码中,'name' 是一个私有变量。


class car {
	 	 #name= "TATA";
}

如果您尝试通过类的实例访问名称,则会出现一个错误,即无法在类外部访问私有字段。

为了实现封装,您可以在类中定义私有变量,并使用不同的方法授予它们对外部世界的访问权限。

在下面的示例中,我们定义了 car 类。

car 类包含 'brand'、'name' 和 'milage' 私有变量。

getMilage() 方法被定义为返回汽车的英里数,setMilage() 方法用于设置该方法的英里数。

我们创建了 car 类的对象,并使用该方法访问和修改私有字段。如果您尝试访问类的 private 字段,代码将引发错误。

您还可以在类中定义更多方法来访问和修改其他私有字段。


<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 和 setters 方法的名称是相同的。

之后,我们创建一个类的对象,并使用 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

综上所述,你可以理解 encapsulation 是将变量设为私有并限制其对外部世界的访问。

JavaScript 封装的好处

在这里,我们列出了在 JavaScript 中封装的一些好处 -

  • 数据保护 − 封装允许您通过将类数据设为私有来控制类数据的访问。您只能公开所需的数据和方法。因此,没有人可以错误地修改数据。此外,您还可以在更新数据时验证数据。如果新数据无效,则可以引发错误。
  • 代码可重用性 − 类是对象的模板,您可以重用它来创建具有不同数据的对象。
  • 代码维护 − 封装使维护代码变得容易,因为每个对象都是独立的,如果您对一个对象进行更改,则不会影响其他代码。