私有的和受保护的属性和方法

55 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第20天,点击查看活动详情

面向对象编程最重要的原则之一 —— 将内部接口与外部接口分隔开来。

在开发比 “hello world” 应用程序更复杂的东西时,这是“必须”遵守的做法。

为了理解这一点,让我们脱离开发过程,把目光转向现实世界。

通常,我们使用的设备都非常复杂。但是,将内部接口与外部接口分隔开来可以让我们使用它们且没有任何问题。

一个现实生活中的例子

例如,一个咖啡机。从外面看很简单:一个按钮,一个显示器,几个洞……当然,结果就是 —— 很棒的咖啡!:)

但是在内部……(一张摘自维修手册的图片)

有非常多的细节。但我们可以在完全不了解这些内部细节的情况下使用它。

咖啡机非常可靠,不是吗?一台咖啡机我们可以使用好几年,只有在出现问题时 —— 把它送去维修。

咖啡机的可靠性和简洁性的秘诀 —— 所有细节都经过精心校并 隐藏 在内部。

如果我们从咖啡机上取下保护罩,那么使用它将变得复杂得多(要按哪里?),并且很危险(会触电)。

正如我们所看到的,在编程中,对象就像咖啡机。

但是为了隐藏内部细节,我们不会使用保护罩,而是使用语言和约定中的特殊语法。

内部接口和外部接口

在面向对象的编程中,属性和方法分为两组:

  • 内部接口 —— 可以通过该类的其他方法访问,但不能从外部访问的方法和属性。
  • 外部接口 —— 也可以从类的外部访问的方法和属性。

如果我们继续用咖啡机进行类比 —— 内部隐藏的内容:锅炉管,加热元件等 —— 是咖啡机的内部接口。

内部接口用于对象工作,它的细节相互使用。例如,锅炉管连接到加热元件。

但是从外面看,一台咖啡机被保护壳罩住了,所以没有人可以接触到其内部接口。细节信息被隐藏起来并且无法访问。我们可以通过外部接口使用它的功能。

所以,我们需要使用一个对象时只需知道它的外部接口。我们可能完全不知道它的内部是如何工作的,这太好了。

这是个概括性的介绍。

在 JavaScript 中,有两种类型的对象字段(属性和方法):

  • 公共的:可从任何地方访问。它们构成了外部接口。到目前为止,我们只使用了公共的属性和方法。
  • 私有的:只能从类的内部访问。这些用于内部接口。

在许多其他编程语言中,还存在“受保护”的字段:只能从类的内部和基于其扩展的类的内部访问(例如私有的,但可以从继承的类进行访问)。它们对于内部接口也很有用。从某种意义上讲,它们比私有的属性和方法更为广泛,因为我们通常希望继承类来访问它们。

受保护的字段不是在语言级别的 Javascript 中实现的,但实际上它们非常方便,因为它们是在 Javascript 中模拟的类定义语法。

现在,我们将使用所有这些类型的属性在 Javascript 中制作咖啡机。咖啡机有很多细节,我们不会对它们进行全面模拟以保持简洁(尽管我们可以)。

受保护的 “waterAmount”

首先,让我们做一个简单的咖啡机类:

class CoffeeMachine {
  waterAmount = 0; // 内部的水量

  constructor(power) {
    this.power = power;
    alert( `Created a coffee-machine, power: ${power}` );
  }

}

// 创建咖啡机
let coffeeMachine = new CoffeeMachine(100);

// 加水
coffeeMachine.waterAmount = 200;

现在,属性 waterAmount 和 power 是公共的。我们可以轻松地从外部将它们 get/set 成任何值。

让我们将 waterAmount 属性更改为受保护的属性,以对其进行更多控制。例如,我们不希望任何人将它的值设置为小于零的数。

受保护的属性通常以下划线 _ 作为前缀。

这不是在语言级别强制实施的,但是程序员之间有一个众所周知的约定,即不应该从外部访问此类型的属性和方法。

所以我们的属性将被命名为 _waterAmount

class CoffeeMachine {
  _waterAmount = 0;

  set waterAmount(value) {
    if (value < 0) {
      value = 0;
    }
    this._waterAmount = value;
  }

  get waterAmount() {
    return this._waterAmount;
  }

  constructor(power) {
    this._power = power;
  }

}

// 创建咖啡机
let coffeeMachine = new CoffeeMachine(100);

// 加水
coffeeMachine.waterAmount = -10; // _waterAmount 将变为 0,而不是 -10

现在访问已受到控制,因此将水量的值设置为小于零的数变得不可能。