类型错误:使用私有字段/方法两次初始化对象是错误的

当通过类构造函数创建的对象再次经历类构造并且该类包含 私有属性 时,会发生 JavaScript 异常 "使用私有字段/方法两次初始化对象是错误的"。这通常是由 返回覆盖 技巧引起的。

¥The JavaScript exception "Initializing an object twice is an error with private fields/methods" occurs when an object that was created via a class constructor goes through the class construction again, and the class contains a private property. This is usually caused by the return override trick.

信息

¥Message

TypeError: Cannot initialize #x twice on the same object (V8-based)
TypeError: Initializing an object twice is an error with private fields (Firefox)
TypeError: Cannot redefine existing private field (evaluating 'super(o)') (Safari)

TypeError: Cannot initialize private methods of class X twice on the same object (V8-based)
TypeError: Initializing an object twice is an error with private methods (Firefox)
TypeError: Cannot install same private methods on object more than once (evaluating 'super(o)') (Safari)

错误类型

¥Error type

TypeError

什么地方出了错?

¥What went wrong?

对于任何对象,如果它已经包含私有字段或方法,则再次安装相同字段将是一个错误。在调用类构造函数时,私有属性会安装在 this 的值上,因此如果 this 值是该类的已构造实例,则可能会发生此错误。

¥For any object, if it already contains a private field or method, it would be an error to install the same field again. Private properties are installed on the value of this when the class constructor is called, so this error could happen if the this value is an already-constructed instance of this class.

通常,构造函数中的 this 是一个新创建的对象,它没有任何预先存在的属性。但是,它可以被基类的返回值覆盖。如果基类返回另一个对象,该对象将替换当前对象作为 this 的值:

¥Usually, this in a constructor is a newly created object which doesn't have any preexisting properties. However, it can be overridden by the return value of the base class. If the base class returns another object, that object would replace the current object as the value of this:

js
class Base {
  constructor(o) {
    // This object will become the this value of any subclass
    return o;
  }
}

class Derived extends Base {
  #x = 0;
}

如果你调用 new Derived(anyObject),而 anyObject 不是 Derived 的实例,则将使用 anyObject 作为 this 值来调用 Derived 构造函数,因此在 anyObject 上安装 #x 私有字段。这是 "返回覆盖" 技巧,它允许你定义不相关对象的任意信息。但是,如果你调用 new Derived(new Derived()),或再次调用 new Derived(anyObject)Derived 构造函数将尝试在已经具有 #x 私有字段的对象上再次安装 #x 私有字段,从而导致此错误。

¥If you call new Derived(anyObject), where anyObject is not an instance of Derived, the Derived constructor will be called with anyObject as the this value, and therefore install the #x private field on anyObject. This is the "return override" trick, which allows you to define arbitrary information on unrelated objects. However, if you call new Derived(new Derived()), or call new Derived(anyObject) again, the Derived constructor will try to install the #x private field again on an object which already has the #x private field, causing this error.

也可以看看

¥See also