TypeError: can't access/set private field or method: object is not the right class
当私有字段或方法在未定义该私有元素的对象上进行获取或设置时,会发生 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)
错误类型
TypeError
哪里出错了?
你正在尝试在一个对象上获取或设置私有字段或方法,但该对象不包含此私有元素。私有实例属性只能在其声明的类的实例(包括其子类)上访问;私有静态属性只能在其声明的类本身上访问,而不能在子类上访问。
当私有名称存在于类作用域中,但访问它的对象无效时,会发生此错误。如果私有名称不存在,你将得到一个语法错误。
示例
静态/实例字段不匹配
你可能将字段声明为静态字段,但却尝试在实例上访问它,反之亦然。
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
要解决此问题,可以更改字段为实例字段,或者在类本身上访问该字段,或者在实例上声明另一个字段。请注意,私有命名空间在静态属性和实例属性之间共享,因此不能拥有同名的静态和实例私有元素。
class MyClass {
#x = 0;
doSomething() {
console.log(this.#x);
}
}
class MyClass2 {
static #x = 0;
doSomething() {
console.log(MyClass2.#x);
}
}
使用了错误的对象
你可能有一个访问 this.#x 的方法,但它被以另一个 this 值调用了。
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.func 以正确的 this 值被调用。
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 转换为对象)。此错误仅在方法以不同的对象作为 this 被调用时发生,无论是通过使用 call() 或 apply(),还是通过将方法作为回调函数传递给以不同 this 值调用它的函数。
如果你不确定对象是否包含私有元素,如下面的代码所示:
class MyClass {
#x = 0;
static doSomething(obj) {
console.log(obj.#x); // Throws if obj is not an instance of MyClass
}
}
你可以首先使用 in 运算符执行品牌检查。
class MyClass {
#x = 0;
static doSomething(obj) {
if (!(#x in obj)) {
return;
}
console.log(obj.#x);
}
}
在子类上访问静态元素
如果你有一个私有静态属性,你只能在声明它的类上访问它,而不能在子类上访问。
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 访问私有静态属性。相反,请始终明确指定类的名称。
class MyClass {
static #x = 0;
doSomething() {
console.log(MyClass.#x);
}
}
访问另一个类中同名的私有元素
与普通字符串或 Symbol 属性不同,私有名称在类之间不共享。如果两个类中都有同名的私有元素,它们仍然不是相同的元素,你不能从一个类访问另一个类的私有元素。
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
向不相关对象添加私有元素
你不能动态地添加私有元素到不相关的对象。
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。
class MyClass {
static #objToX = new WeakMap();
static stamp(obj) {
MyClass.#objToX.set(obj, 1);
}
}
MyClass.stamp({});