跳至主要内容

私有品牌檢查,也就是 `#foo in obj`

· 閱讀時間約 3 分鐘
Marja Hölttä ([@marjakh](https://twitter.com/marjakh))

The in operator 可用於測試給定的物件(或其原型鏈上的任何物件)是否具有給定屬性:

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; 它不是相同的 #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, 私有字段是繼承的而不是屬於自身
A.test(o.__proto__); // true

const o2 = {};
Object.setPrototypeOf(o2, a);
A.test(o2); // false, 私有字段是繼承的而不是屬於自身
A.test(o2.__proto__); // true

訪問不存在的私有字段會拋出錯誤——與正常屬性不同,當訪問不存在的屬性時會返回 undefined 而不拋出錯誤。在私有品牌檢查之前,開發者被迫使用 try-catch 來實現需要的私有字段不存在的情況下的回退行為:

class D {
  use(obj) {
    try {
      obj.#foo;
    } catch {
      // 回退情況:obj 沒有 #foo
    }
  }
  #foo = 0;
}

現在可以使用私有品牌檢查來測試私有字段是否存在:

class E {
  use(obj) {
    if (#foo in obj) {
      obj.#foo;
    } else {
     // 回退情況:obj 沒有 #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();

私有品牌檢查支持