TypeError: 无法访问/设置私有字段或方法:对象不是正确的类

当在未定义此私有属性的对象上获取或设置私有字段或方法时,会出现JavaScript异常“无法访问私有字段或方法:对象不是正确的类”或“无法设置私有字段:对象不是正确的类”。

消息

TypeError: Cannot read private member #x from an object whose class did not declare it (V8-based)
TypeError: Cannot write private member #x to an object whose class did not declare it (V8-based)
TypeError: can't access private field or method: object is not the right class (Firefox)
TypeError: can't set private field: object is not the right class (Firefox)
TypeError: Cannot access invalid private field (evaluating 'this.#x') (Safari)

错误类型

出了什么问题?

您正在尝试获取或设置对象上的私有字段或方法,但该对象不包含此私有属性。私有实例属性只能在声明它们的类的实例(包括其子类)上访问;私有静态属性只能在声明它们的类本身(而不是子类)上访问。

当私有名称存在于类范围内但访问它的对象无效时,就会发生此错误。如果私有名称不存在,则会改为出现语法错误

示例

静态/实例字段不匹配

您可能已将字段声明为静态字段,但尝试在实例上访问它,反之亦然。

js
class MyClass {
  static #x = 0;
  doSomething() {
    console.log(this.#x);
  }
}

const obj = new MyClass();
obj.doSomething();
// TypeError: can't access private field: object is not the right class

要解决此问题,请更改字段为实例字段,或在类本身访问该字段,或在实例上声明另一个字段。请注意,私有命名空间在静态和实例属性之间共享,因此您不能使用相同名称的静态和实例私有属性。

js
class MyClass {
  #x = 0;
  doSomething() {
    console.log(this.#x);
  }
}

class MyClass2 {
  static #x = 0;
  doSomething() {
    console.log(MyClass2.#x);
  }
}

使用了错误的对象

也许您有一个访问this.#x的方法,但它使用另一个this值调用。

js
class JSONReplacer {
  #count = 0;
  func(key, value) {
    if (typeof value === "object") {
      this.#count++;
    }
    return value;
  }
}

JSON.stringify({ a: 1, b: { c: 2 } }, new JSONReplacer().func);
// TypeError: can't access private field: object is not the right class

这是因为JSON.stringify()使用包含value作为this的对象调用replacer函数,因此无法访问私有字段。要解决此问题,您可以将方法绑定到对象,或使用箭头函数,以确保replacer.func使用正确的this值调用。

js
const replacer = new JSONReplacer();
JSON.stringify({ a: 1, b: { c: 2 } }, replacer.func.bind(replacer));
JSON.stringify({ a: 1, b: { c: 2 } }, (...args) => replacer.func(...args));

大多数情况下,如果您意外地解绑了一个方法,则该方法将使用undefined作为this调用,这将导致不同的错误(TypeError: 无法将undefined转换为对象)。此错误仅在使用call()apply()调用方法,或者将方法作为回调传递给使用其他this值调用它的函数时发生。

如果您不确定对象是否包含私有属性,如下面的代码所示

js
class MyClass {
  #x = 0;
  static doSomething(obj) {
    console.log(obj.#x); // Throws if obj is not an instance of MyClass
  }
}

您可以使用in运算符首先执行品牌检查

js
class MyClass {
  #x = 0;
  static doSomething(obj) {
    if (!(#x in obj)) {
      return;
    }
    console.log(obj.#x);
  }
}

在子类上访问静态属性

如果您有一个私有静态属性,则只能在声明它的类上访问它,而不能在子类上访问它。

js
class MyClass {
  static #x = 0;
  doSomething() {
    console.log(this.#x);
  }
}

class MySubClass extends MyClass {}

MySubClass.doSomething();
// TypeError: can't access private field: object is not the right class

要解决此问题,切勿通过this访问私有静态属性。而是始终显式指定类的名称。

js
class MyClass {
  static #x = 0;
  doSomething() {
    console.log(MyClass.#x);
  }
}

在另一个类上访问同名私有属性

与普通字符串或符号属性不同,私有名称不会在类之间共享。如果两个类中都有一个同名的私有属性,它们仍然不是同一个属性,并且您无法从另一个类访问一个类的私有属性。

js
class MyClass {
  #x = 0;
}

class MyOtherClass {
  #x = 1;
  doSomething(o) {
    console.log(o.#x);
  }
}

const obj = new MyClass();
new MyOtherClass().doSomething(obj);
// TypeError: can't access private field: object is not the right class

向不相关的对象添加私有属性

您无法动态添加不相关的对象的私有属性。

js
class MyClass {
  #x = 0;
  static stamp(obj) {
    obj.#x = 1;
  }
}

MyClass.stamp({});
// TypeError: can't set private field: object is not the right class

如果您确实需要这样做,请考虑返回覆盖技巧。但是,通常,您可能希望改用WeakMap

js
class MyClass {
  static #objToX = new WeakMap();
  static stamp(obj) {
    MyClass.#objToX.set(obj, 1);
  }
}

MyClass.stamp({});

另请参阅