JavaScript 私人会员 - 没有第 3 阶段预设

JavaScript private members - Without stage 3 presets

我们在 Java 和许多其他 Object 面向编程语言中有私有成员。因此,问题的标题很明确:我们如何在 JavaScript 类 中拥有私有成员?

让我们考虑以下示例:

// PrivateMemberOwner.js
export default class PrivateMemberOwner {
    iAmPrivate () {
        console.log("I can not be accessed from Foo class' children!");
    }
    iAmPublic () {
        console.log("Anyone can call me from anywhere!");
    }
}

然后在我的 PrivateMemberOwnerExtension.js:

import PrivateMemberOwner from "./PrivateMemberOwner.js";

export default class PrivateMemberOwnerExtension extends PrivateMemberOwner {
    consumer () {
        // This one should work just fine:
        Foo.iAmPublic();

        // This one should throw some sort of error
        Foo.iAmPrivate();
    }
}

正确的处理方法是什么?

在我看来,在JavaScript中拥有私有成员应该满足这些条件:

  1. 能够在 PrivateMemberOwner class 内调用 iAmPrivate。 (如问题所述)
  2. 无法在 class 定义之外的任何地方调用 iAmPrivate。 (至少不能不小心调用)
  3. 该机制应利用 JavaScript 的原型继承功能以消耗更少的内存。

话虽如此,我想到了 ES6 中引入的新 Symbol 类型。

Symbols are a primitive type introduced in ECMAScript 6, joining the existing primitive types: strings, numbers, booleans, null, and undefined.

(来自 Understanding ECMAScript 6 - 作者 Nicholas C. Zakas

所以,让我们来看看代码:

// PrivateMemberOwner.js

const iAmPrivate = Symbol("iAmPrivate");

export default class PrivateMemberOwner {
    [iAmPrivate] (message) {
        console.log("I can not be accessed from Foo class' children!", message);
    }
    iAmPublic () {
        console.log("Anyone can call me from anywhere!");
    }
    iAmPrivateUser () {
        this[iAmPrivate]("I can access 'iAmPrivate' just fine.");
    }
}

这样,我们可以让iAmPrivateUser方法访问iAmPrivate方法,让PrivateMemberOwnerclass的children无法访问它。 (至少是偶然访问到的)

备注

  1. 如上所述,这类成员不是完全私有的,[需要一些努力]可以像这样访问成员:

    // ./PrivateReader.js import PrivateMemberOwner from "./PrivateMemberOwner.js"; const privateMemberOwner = new PrivateMemberOwner(); const privateMemberOwnerSymbols = Object.getOwnPropertySymbols(privateMemberOwner); const iWasPrivate = privateMemberOwner[privateMemberOwnerSymbols[0]]; iWasPrivate.call(privateMemberOwner, 'I just gained your access!');

  2. 私有属性也可以用同样的方法实现:

    const barSymbol = Symbol('bar'); class Foo { constructor () { this[barSymbol] = null; } getBar () { return this[barSymbol]; } setBar (value) { this[barSymbol] = value; } }