首页 > 解决方案 > 我可以使用变量作为标识符来设置私有类字段吗?如何?

问题描述

Node.js 12 支持由# out-of-the-box 表示的私有类字段,没有标志或转译器。

例如,这适用于 Node.js 12:

class Foo {
  #bar = 1;

  constructor({ bar }) {
    this.#bar = bar;
  }

  get bar() {
    return this.#bar;
  }
}

const foo = new Foo({ bar: 2 });

console.log(foo.bar); // 2

假设我想用 20 而不是 1 个属性来构造我的 Foo 实例——我必须将构造函数和 getter 函数中的赋值语句复制 20 次,这会产生很多样板代码。

如果我不使用私有字段而是使用常规类字段,这不难避免:

class Foo {
  bar = 1;

  constructor(properties) {
    Object.entries(properties).forEach(([name, value]) => (this[name] = value));
  }

  get bar() {
    return this.bar;
  }
}

const foo = new Foo({ bar: 2 });

console.log(foo.bar); // 2

但是,对于私有类字段,它不起作用:

class Foo {
  #bar = 1;

  constructor(properties) {
    Object.entries(properties).forEach(
      ([name, value]) => (this[`#${name}`] = value)
    );
  }

  get bar() {
    return this.#bar;
  }
}

const foo = new Foo({ bar: 2 });

console.log(foo.bar); // 1 :-(

我还尝试使用Reflect.set为构造函数中的私有类字段赋值,但无济于事:

class Foo {
  #bar = 1;

  constructor(properties) {
    Object.entries(properties).forEach(([name, value]) =>
      Reflect.set(this, `#${name}`, value)
    );
  }

  get bar() {
    return this.#bar;
  }
}

const foo = new Foo({ bar: 2 });

console.log(foo.bar); // 1 :-(

我可以使用变量作为标识符来设置私有类字段吗?如果是,如何?

标签: javascriptnode.jsprivateecmascript-nextclass-fields

解决方案


不,这看起来不可能。从提案常见问题解答

为什么 this['#x'] 不访问名为 #x 的私有字段,因为 this.#x 可以?

  1. 这会使属性访问语义复杂化。

  2. 对私有字段的动态访问与“私有”的概念相反。例如,这是关于:

class Dict extends null {
  #data = something_secret;
  add(key, value) {
    this[key] = value;
  }
  get(key) {
    return this[key];
  }
}
(new Dict).get('#data'); // returns something_secret

语法是这样的,每个私有字段必须#在文字属性名称之前初始化和/或引用,仅此而已。甚至不允许使用括号表示法。

拥有一个名为 x 的私有字段不能阻止有一个名为 x 的公共字段,因此访问私有字段不能只是正常的查找。

除非在类主体中明确定义(不是类函数,而是直接在类主体中),否则您甚至不能引用私有字段:

class Foo {
  // error is thrown because #abc must be defined in this section
  doSomething() {
    return this.#abc;
  }
}

也就是说,没有什么能阻止你创建一个私有属性,它是一个对象,所有这些属性都在对象上:

class Foo {
  #privates = {};

  constructor(properties) {
    Object.entries(properties).forEach(
      ([name, value]) => (this.#privates[name] = value)
    );
  }

  get privates() {
    return this.#privates;
  }
}

const foo = new Foo({ bar: 2 });

console.log(foo.privates.bar);


推荐阅读