JavaScript 中的私有类字段

Posted

技术标签:

【中文标题】JavaScript 中的私有类字段【英文标题】:Private class field in JavaScript 【发布时间】:2021-08-27 10:34:12 【问题描述】:
class A 
  #a = 1;
  static #a = 2;

结果

Uncaught SyntaxError: redeclaration of private name #a 在 Firefox 中 Uncaught SyntaxError: Identifier '#a' has already been declared 在 Chrome 中

虽然

class A 
  a = 1;
  static a = 2;

在 Firefox 和 Chrome 中都有效

AFAIK 实例字段将安装在类实例上,而静态字段将安装在类对象本身上。他们并不冲突。为什么以前的代码无效?

【问题讨论】:

【参考方案1】:

在带有# 前缀的类中声明的任何属性都被视为私有属性。但是 javascript 设计中没有私有属性的概念。但是,我们可以通过在函数外部定义私有属性然后在函数内部使用它们来模拟私有属性。这样,除非我们导出,否则这些属性无法在模块外部访问。所以,上面代码的转译版本会是这样的。

var _a = /*#__PURE__*/new WeakMap();
var A = function A() 
   _classCallCheck(this, A);
  _a.set(this, 
    writable: true,
    value: 1
  );
;

如您所见,#a 变量被转换为 _a 并在函数外部声明为 WeakMap 并在函数内部使用。

因此,如果您有另一个同名的静态属性#a,它会尝试在函数外部创建另一个同名的变量_a。由于它是私有静态属性,因此不会附加到函数。所以转译后的代码会是这个样子。

var _a = /*#__PURE__*/new WeakMap();
var A = function A() 
   _classCallCheck(this, A);
  _a.set(this, 
    writable: true,
    value: 1
  );
;

var _a = 
  writable: true,
  value: 2
;

如您所见,它尝试创建另一个具有相同名称的变量并引发错误。这是 javascript 的限制,显然可以通过指定不同的名称来避免。

后一种情况是有效的,因为它们不是私有属性。

【讨论】:

一个特性可以这样 polyfill 并不意味着 JS 引擎会这样实现。有些功能根本无法填充(比如代理)【参考方案2】:

实例字段将安装在类实例上,而静态字段将安装在类对象本身上。它们并不冲突。

它们是,因为在表达式x.#a 中,引擎不知道x 是类对象还是类实例。 #a 标记的含义应该是静态的,并且不依赖于对象,它需要引用一个或另一个而不是两者。

与由标识符的字符串值标识的普通字符串键属性不同(.a.a 相同,无论上下文如何),私有字段具有一个标识(与符号不同),它是用它们创建的宣言。每个class 定义只能存在一个#a,并且在不同的类中,相同的#a 语法将引用不同的字段。

【讨论】:

'它们是,因为在表达式 x.#a 中引擎不知道 x 是类对象还是类实例。引擎在解析时不知道它,但在运行时知道它。我认为这不是问题。 @CarterLi 它可以在运行时执行,但这不是类字段的设计方式 - 它们应该在编译时间。 如何在编译时完全识别它们?引擎甚至不知道 x 是否是一个对象。 标准对此有什么规定吗? @CarterLi 引擎不知道x 是什么,但它确切地知道它试图访问其中的哪个字段(“私人名称”)。该标准非常清楚 #a 需要如何在词法上解析为一个字段(“私人名称”)。【参考方案3】:

两次都使用不同的变量名,例如 ab,而不是 a

我猜这个变量不能同时是静态和非静态的。

仅供参考 - 此处支持私有类字段:https://caniuse.com/mdn-javascript_classes_private_class_fields

【讨论】:

以上是关于JavaScript 中的私有类字段的主要内容,如果未能解决你的问题,请参考以下文章

Javascript 中的私有字段不会出现在 JSON.stringify 中

如何防止修改类中的私有字段?

Delphi:写入后代类中的私有祖先字段

访问类中的私有字段[重复]

用私有属性替换类中的每个字段是一种不好的做法吗? [复制]

C# 类继承中的私有字段都去了哪里?