如何在 nodejs 12 中使用私有 class 字段?

How to use private class fields in nodejs 12?

nodejs12.x.x的当前版本中,我们可以声明privateclass 字段由 #some_varible 表示法。 # 符号将使该特定 class.

的变量私有字段
class Foo {
  #some_varible = 10;
}

我有以下问题:

How to use this variable?

您可以将其用作访问器的支持字段 属性:

class Foo {
  #some_variable = 10;
  
  get some_variable () {
    console.log('getting some_variable');
    return this.#some_variable;
  }
  
  set some_variable (value) {
    console.log('setting some_variable');
    this.#some_variable = value;
  }
}

let bar = new Foo();

console.log(bar.some_variable);
console.log(bar.some_variable = 42);

或者您可以像普通 属性 一样使用它,主要区别在于它只能从 class { ... } 范围内访问,因为它是私有的,并且 属性 以 #.

为前缀

另请注意,this.#some_variablethis['#some_variable']相同,后者指的是字符串属性,而不是私人领域。

Is it safe to use such notations in a production app for declaring private class fields?

答案是这取决于

使用客户端 JavaScript,您无法控制执行环境,但您可以将新语言功能(例如私有 class 字段)转换为仅依赖于旧语言语法的功能等价物使用 Babel plugins.

的规范

在 Node.js 中,您 可以控制执行环境,因此要使用新的语言功能,只需确保您使用的是 a version that supports the syntax

How does it work in background (in nodejs), when some class field is declared private?

V8 实现

私有字段使用块范围symbols which are blacklisted from the reflection method Object.getOwnPropertySymbols()

下面是经过转换的 class 模拟此实现的样子:

let Foo;

{
  const some_variable = Symbol('#some_variable');

  {
    const getOwnPropertySymbolsImpl = Object.getOwnPropertySymbols;
    Object.getOwnPropertySymbols = function getOwnPropertySymbols () {
      return getOwnPropertySymbolsImpl.apply(this, arguments)
        .filter(symbol => symbol !== some_variable);
    };
  }

  Foo = class Foo {
    [some_variable] = 10;
  };
}

let bar = new Foo();
console.log(bar);

这会产生与本机实现非常相似的输出:


Babel 实现

@babel/plugin-proposal-class-properties

私有字段存储为 WeakMap where each entry is a class instance key and its respective private field property descriptor 值。

以下是 Babel 如何转换我们一直用作示例的 class:

class Foo {
  constructor() {
    _some_variable.set(this, {
      writable: true,
      value: 10
    });
  }
}

var _some_variable = new WeakMap();

请注意,虽然 _some_variable 在这里不是块范围的,但 Babel 将确保转换后的输出中的名称不会与该范围内的任何可见名称冲突,因此无法从内部访问弱映射预编译源。