【问题标题】:Can I set a private class field using a variable as identifier? How?我可以使用变量作为标识符来设置私有类字段吗?如何?
【发布时间】:2020-02-06 09:00:48
【问题描述】:

Node.js 12 支持开箱即用的private class fields denoted by #,没有标志或转译器。

例如,这适用于 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

假设我想构建我的 Foo 实例不是使用 1 个属性,而是使用 20 个属性 - 我必须将构造函数和 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 :-(

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

【问题讨论】:

  • 为了将来参考,当您问自己如何声明 20 个变量而不重复代码 20 次时,您通常只需要某种集合,例如数组或字典(例如对象或映射) ),如下所示。
  • @PatrickRoberts 我正在尝试使用私有类字段作为创建不可变对象的一种方式,有没有一种优雅的方法可以通过对象或地图来实现这一点?
  • 我倾向于拒绝。不过,这正是 Object.freeze() 的用途。

标签: javascript node.js private ecmascript-next class-fields


【解决方案1】:

不,这看起来不可能。来自proposal FAQ

既然 this.#x 可以,为什么 this['#x'] 不访问名为 #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);

【讨论】:

  • 有没有办法以编程方式添加设置器,例如使用反射和/或代理?
  • 两者都不需要,你可以用Object.defineProperty来做
猜你喜欢
  • 2013-11-16
  • 2014-07-14
  • 2014-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-02
  • 2018-06-17
相关资源
最近更新 更多