发布于 2026-01-06 3 阅读
0

ECMAScript 类 - 保持私有性

ECMAScript 类 - 保持私有性

最初发布于devinduct

介绍

和往常一样,我们先从一些理论解释开始。ES 类是 JavaScript 中新的语法糖。它们提供了一种简洁的方式来编写和实现与使用原型链相同的功能。唯一的区别在于它看起来更美观,而且如果你之前接触过 C# 或 Java,你会觉得它更自然。有人可能会说它们并非为 JavaScript 而设计,但就我个人而言,我并不介意使用类或 ES5 原型标准。

它们提供了一种更简便的封装方式,可以创建一组固定的方法,用于操作具有有效内部状态的实体。简而言之,我们可以用更少的代码实现更多的功能,这正是关键所在。借助它们,JavaScript 正朝着面向对象的方式发展,通过使用它们,我们将应用程序拆分成对象而不是函数。别误会,将应用程序拆分成函数并非坏事,实际上,这是一件好事,它比类有一些优势,但这又是另一个话题了。

更实际地说,每当我们想在应用程序中描述现实世界的模型时,我们都会使用类来实现。例如,建筑物、汽车、摩托车等等。它们代表现实世界中的实体。

范围

在服务器端语言中,我们有访问修饰符可见性级别,例如public` privatepublic`、protected`public`、` internalpublic`、`public`package等等。遗憾的是,JavaScript 只支持前两种访问修饰符,而且支持的方式也各不相同。我们不会使用 `public`public或 ` privatepublic` 来声明字段,JavaScript 在某种程度上假定所有作用域都是 `public` 的,这也是我写这篇文章的原因。

请注意,我们可以在类中声明私有字段和公共字段,但这些字段声明是一项实验性功能,因此目前还不能安全使用。

class SimCard {
  number; // public field
  type; // public field
  #pinCode; // private field
}

如果没有像 Babel 这样的编译器,则不支持像上面这样的字段声明

保护隐私——封装

在编程中,封装是指将某些内容保护起来,使其对外部世界隐藏。通过将数据设为私有,使其仅对所有者可见,我们就实现了数据封装。本文将介绍几种数据封装方法。让我们深入了解一下。

1. 根据惯例

这实际上只是伪造private数据或变量的状态。实际上,它们是公开的,任何人都可以访问。我遇到的两种最常见的私有化约定是 `-p`$和 ` _-v` 前缀。如果某个属性以这些符号之一作为前缀(通常整个应用程序只会使用一个),则应将其视为该特定对象的非公共属性。

class SimCard {
  constructor(number, type, pinCode) {
    this.number = number;
    this.type = type;

    // this property is intended to be a private one
    this._pinCode = pinCode;
  }
}

const card = new SimCard("444-555-666", "Micro SIM", 1515);

// here we would have access to the private _pinCode property which is not the desired behavior
console.log(card._pinCode); // outputs 1515

2. 隐私与封闭

闭包在维护变量作用域方面非常有用。它们由来已久,几十年来一直被 JavaScript 开发者使用。这种方法能真正保护隐私,数据不会被外部访问,只能由所有者实体管理。我们将在类构造函数中创建局部变量,并使用闭包将其捕获。为了使其生效,这些方法必须附加到实例上,而不是定义在原型链上。

class SimCard {
  constructor(number, type, pinCode) {
    this.number = number;
    this.type = type;

    let _pinCode = pinCode;
    // this property is intended to be a private one
    this.getPinCode = () => {
        return _pinCode;
    };
  }
}

const card = new SimCard("444-555-666", "Nano SIM", 1515);
console.log(card._pinCode); // outputs undefined
console.log(card.getPinCode()); // outputs 1515

3. 使用符号和 Getter 实现隐私

Symbol 是 JavaScript 中的一种新的原始数据类型。它在 ECMAScript 版本 6 中引入。每次调用返回的值Symbol()都是唯一的,这种类型的主要用途是用作对象属性标识符。

由于我们的目标是在类定义之外创建符号,但又不想将其设为全局符号,因此引入了模块。这样,我们就可以在模块级别创建私有字段,在构造函数中将其附加到类对象,并从类的 getter 方法中返回符号键。需要注意的是,我们也可以使用原型链上创建的标准方法,而不是使用 getter 方法。我之所以选择使用 getter 方法,是因为我们不需要调用函数来获取值。

const SimCard = (() => {
  const _pinCode = Symbol('PinCode');

  class SimCard {
    constructor(number, type, pinCode) {
      this.number = number;
      this.type = type;
      this[_pinCode] = pinCode;
    }

    get pinCode() {
       return this[_pinCode];
    }
  }

  return SimCard;
})();

const card = new SimCard("444-555-666", "Nano SIM", 1515);
console.log(card._pinCode); // outputs undefined
console.log(card.pinCode); // outputs 1515

这里需要指出的一点是这个Object.getOwnPropertySymbols方法。这个方法可以用来访问我们原本打算设为私有的字段。_pinCode我们可以这样从类中检索值:

const card = new SimCard("444-555-666", "Nano SIM", 1515);
console.log(card[Object.getOwnPropertySymbols(card)[0]]); // outputs 1515

4. 使用 Wea​​kMap 和 Getters 保护隐私

Map它们WeakMap也在 ECMAScript 版本 6 中引入。它们以键值对格式存储数据,因此非常适合存储私有变量。在我们的示例中,WeakMap每个属性都在模块级别定义了一个私有属性,并在类构造函数中设置了每个私钥。值由类的 getter 方法获取,之所以选择这种方法,是因为我们不需要调用函数来获取值。

const SimCard = (() => {
  const _pinCode = new WeakMap();
  const _pukCode = new WeakMap();

  class SimCard {
    constructor(number, type, pinCode, pukCode) {
      this.number = number;
      this.type = type;
      _pinCode.set(this, pinCode);
      _pukCode.set(this, pukCode);
    }

    get pinCode() {
       return _pinCode.get(this);
    }

    get pukCode() {
       return _pukCode.get(this);
    }
  }

  return SimCard;
})();

const card = new SimCard("444-555-666", "Nano SIM", 1515, 45874589);
console.log(card.pinCode); // outputs 1515
console.log(card.pukCode); // outputs 45874589
console.log(card._pinCode); // outputs undefined
console.log(card._pukCode); // outputs undefined

结论与延伸阅读

希望这些例子对您有所帮助,并能融入到您的工作流程中。如果您觉得有用,并且喜欢这篇文章,请分享出去。

如需进一步阅读,我推荐这篇关于JavaScript 代码整洁最佳实践的文章。

感谢阅读,我们下篇文章再见。

文章来源:https://dev.to/proticm/ecmascript-classes-keeping-things-private-1d34