in
运算符可用于测试给定对象(或其原型链中的任何对象)是否具有给定属性
const o1 = {'foo': 0};
console.log('foo' in o1); // true
const o2 = {};
console.log('foo' in o2); // false
const o3 = Object.create(o1);
console.log('foo' in o3); // true
私有品牌检查功能扩展了 in
运算符,以支持 私有类字段
class A {
static test(obj) {
console.log(#foo in obj);
}
#foo = 0;
}
A.test(new A()); // true
A.test({}); // false
class B {
#foo = 0;
}
A.test(new B()); // false; it's not the same #foo
由于私有名称仅在定义它们的类内部可用,因此测试也必须在类内部进行,例如在像上面 static test
这样的方法中。
子类实例从父类接收私有字段作为自身属性
class SubA extends A {};
A.test(new SubA()); // true
但使用 Object.create
创建的对象(或通过 __proto__
设置器或 Object.setPrototypeOf
稍后设置原型的对象)不会将私有字段作为自身属性接收。由于私有字段查找仅对自身属性有效,因此 in
运算符不会找到这些继承的字段
const a = new A();
const o = Object.create(a);
A.test(o); // false, private field is inherited and not owned
A.test(o.__proto__); // true
const o2 = {};
Object.setPrototypeOf(o2, a);
A.test(o2); // false, private field is inherited and not owned
A.test(o2.__proto__); // true
访问不存在的私有字段会抛出错误 - 与普通属性不同,访问不存在的属性会返回 undefined
但不会抛出错误。在私有品牌检查之前,开发人员被迫使用 try
-catch
来实现对象没有所需私有字段情况下的回退行为
class D {
use(obj) {
try {
obj.#foo;
} catch {
// Fallback for the case obj didn't have #foo
}
}
#foo = 0;
}
现在可以使用私有品牌检查来测试私有字段是否存在
class E {
use(obj) {
if (#foo in obj) {
obj.#foo;
} else {
// Fallback for the case obj didn't have #foo
}
}
#foo = 0;
}
但要注意 - 一个私有字段的存在并不保证对象具有类中声明的所有私有字段!以下示例显示了一个半构造的对象,它只具有其类中声明的两个私有字段之一
let halfConstructed;
class F {
m() {
console.log(#x in this); // true
console.log(#y in this); // false
}
#x = 0;
#y = (() => {
halfConstructed = this;
throw 'error';
})();
}
try {
new F();
} catch {}
halfConstructed.m();
私有品牌检查支持 #
- Chrome: 从版本 91 开始支持
- Firefox: 不支持
- Safari: 不支持
- Node.js: 不支持
- Babel: 不支持