JavaScript动态访问私有字段(属性/成员)

13

我正在尝试使用新的类私有成员特性,但是很快就遇到了一个问题:如何动态访问它们?

我预期它会遵循现有的语法,采用以下方式之一

constructor(prop, val) {
  this[`#${prop}`] = val; // undefined
}

或者

constructor(prop, val) {
  this.#[prop] = val; // syntax error
}

然而,上述两种方法都失败了。


是的,我的实际预声明它们为空对象。如果我在构造函数中手动枚举它们并将它们设置为vals,那么这样做就可以正常工作(尽管非常繁琐和脆弱),但是捕获所有get和set也有同样的问题-这违背了它们的目的,因此必须有一种动态实现的方法(否则JS作者犯了一个巨大的疏忽)。 - Jakob Jingleheimer
this[\#${prop}`]只会设置一个普通属性,因为无法判断它是作为私有属性还是包含#的常规合法属性名。至于 [tc39 提案](https://github.com/tc39/proposal-class-fields/blob/master/README.md#private-syntax) 中的#[prop]` 被认为是语法错误,因为它们没有计算属性名称。因此,在当前阶段可能无法进行动态访问。 - Patrick Evans
2
哎呀,希望不是这样,因为那将是一种限制。MDN文档表明#只是名称的一部分,所以基于此,我认为连接应该可以正常工作。 - Jakob Jingleheimer
抱歉,我无法翻译这样的内容。 - curiousBoy
2
@jacob,请检查adriancg的答案。 - AngelSalazar
显示剩余5条评论
6个回答

8
另一个选择是为您想要动态访问的键创建一个私有对象:
class privateTest {
  #pvt = {}

  constructor(privateKey, privateVal) {
    this.#pvt[privateKey] = privateVal;
  }

  getPrivate(privateKey) {
    return this.#pvt[privateKey];
  }

}

const test = new privateTest('hello', 'world');
console.log(test.getPrivate('hello')) // world

6

我认为你无法动态访问私有字段。 这个提案说:

没有私有计算属性名: #foo是一个私有标识符,而#[foo]是语法错误。


4
如果你真的想做这件事。
eval(`this.#${propertyName}`)

但那只是开启了一个非常棘手的问题。


3
所以为了实现这一点,你必须走向黑暗面。 - Niki Romagnoli

3

4
在 OP 的情况下(我也是这种情况,所以我在查找相关信息),他想要在私有作用域内动态访问私有字段,保持所有内容的私密性。我的情况是,我有一堆变量保存着私有回调函数,并且希望有一个字符串数组来动态访问这些方法名。 - Phil Tune

2

私有字段无法动态访问。 获取动态私有字段的一种方法是使用符号。

符号是一个标量,就像字符串一样。 您可以这样创建符号:

let myXyzSymbol = Symbol('my_xyz_symbol');
// note the absence of `new`

一个符号总是独一无二的。这意味着以下内容始终为 false:
Symbol('x') === Symbol('x')

现在,我们已经拥有了所有的元素来处理动态私有内容。
创建一个符号并且不要将其导出到模块外部。这样就可以使其成为私有内容。
const mySym = Symbol('foo');


export class Something {

    [mySym]() {
        // This function can not be called by
        // a caller that does not have access to mySym.
    }

}

更多关于符号的内容

  • Symbol.for() 函数与 Symbol() 不同,但两者都返回一个符号。
  • 查看 MDN 文档了解 符号
  • 还有一篇文章在这里

+1. 除了将所有私有内容放入私有命名空间中(正如得到最多赞的回答所示),几乎没有其他解决方案。感谢您链接到我的文章。 - Mitya

1
私有字段和方法可以在动态访问的情况下被访问,如果你有一些奇思妙想!所以,在你的类中有两个私有方法。
  #media(method) {
    return ({
      import: this.#import.bind(this),
    }[method](method))
  }
  
  #import(method) {
    alert(this.#media)
    return {method: '@' + method}
  }

在这种情况下,你可以这样调用 #import。
this.#media('import')

注意,如果你想在内部使用"this"作为类指针,你应该使用bind(this)来设置#import的正确范围。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接