私有元素

私有元素是常規公共類元素的對應物,包括類欄位、類方法等。私有元素透過使用雜湊 # 字首建立,不能在類外部合法引用。這些類元素的隱私封裝由 JavaScript 本身強制執行。訪問私有元素的唯一方法是透過點表示法,並且只能在定義私有元素的類中進行訪問。

在此語法出現之前,私有元素並非語言原生。在原型繼承中,其行為可以透過WeakMap 物件或閉包來模擬,但它們在人體工程學方面無法與 # 語法相提並論。

注意:在 MDN 上,我們避免使用“私有屬性”一詞。JavaScript 中的屬性具有字串或符號鍵,並具有 writableenumerableconfigurable 等特性,但私有元素沒有這些特性。雖然私有元素透過熟悉的點表示法訪問,但它們不能被代理、列舉、刪除,也不能使用任何Object方法進行互動。

語法

js
class ClassWithPrivate {
  #privateField;
  #privateFieldWithInitializer = 42;

  #privateMethod() {
    // …
  }

  static #privateStaticField;
  static #privateStaticFieldWithInitializer = 42;

  static #privateStaticMethod() {
    // …
  }
}

還有一些額外的語法限制

  • 在一個類中宣告的所有私有識別符號必須是唯一的。名稱空間在靜態元素和例項元素之間共享。唯一的例外是當兩個宣告定義一個 getter-setter 對時。
  • 私有識別符號不能是 #constructor

描述

大多數類元素都有它們的私有對應物

  • 私有欄位
  • 私有方法
  • 私有靜態欄位
  • 私有靜態方法
  • 私有 getter
  • 私有 setter
  • 私有靜態 getter
  • 私有靜態 setter

這些特性統稱為私有元素。然而,JavaScript 中的建構函式不能是私有的。為了防止類在類外部被構造,你必須使用一個私有標誌

私有元素用# 名稱(發音為“雜湊名稱”)宣告,它們是帶有 # 字首的識別符號。雜湊字首是屬性名稱的固有部分——你可以將其與舊的下劃線字首約定 _privateField 建立關係——但它不是普通的字串屬性,因此你不能使用方括號表示法動態訪問它。

在類外部引用 # 名稱是語法錯誤。引用未在類體中宣告的私有元素,或嘗試使用delete刪除已宣告的元素也是語法錯誤。

js
class ClassWithPrivateField {
  #privateField;

  constructor() {
    delete this.#privateField; // Syntax error
    this.#undeclaredField = 42; // Syntax error
  }
}

const instance = new ClassWithPrivateField();
instance.#privateField; // Syntax error

JavaScript 作為一種動態語言,能夠執行這種編譯時檢查,因為它使用了特殊的雜湊識別符號語法,使其在語法層面與普通屬性不同。

注意:在 Chrome 控制檯中執行的程式碼可以訪問類外部的私有元素。這是 DevTools 專屬的 JavaScript 語法限制放寬。

如果你從不包含該元素的𝘦𝘮𝘰𝘫𝘪物件訪問私有元素,則會丟擲 TypeError,而不是像普通屬性那樣返回 undefined

js
class C {
  #x;

  static getX(obj) {
    return obj.#x;
  }
}

console.log(C.getX(new C())); // undefined
console.log(C.getX({})); // TypeError: Cannot read private member #x from an object whose class did not declare it

此示例還說明你也可以在靜態函式中訪問私有元素,以及在類外部定義的例項上訪問。

你可以使用 in 運算子檢查外部定義的物件是否擁有私有元素。如果私有欄位或方法存在,這將返回 true,否則返回 false

js
class C {
  #x;
  constructor(x) {
    this.#x = x;
  }
  static getX(obj) {
    if (#x in obj) return obj.#x;

    return "obj must be an instance of C";
  }
}
console.log(C.getX(new C("foo"))); // "foo"
console.log(C.getX(new C(0.196))); // 0.196
console.log(C.getX(new C(new Date()))); // the current date and time
console.log(C.getX({})); // "obj must be an instance of C"

請注意私有名稱始終預先宣告且不可刪除的推論:如果你發現一個物件擁有當前類的一個私有元素(無論是透過 try...catch 還是 in 檢查),它就必須擁有所有其他私有元素。一個物件擁有一個類的私有元素通常意味著它是由該類構造的(儘管並非總是如此)。

私有元素不屬於原型繼承模型,因為它們只能在當前類的 body 中訪問,並且不會被子類繼承。不同類中同名的私有元素完全不同,並且彼此不相容。將它們視為附加到每個例項的外部元資料,由類管理。因此,structuredClone() 不會克隆私有元素,並且Object.freeze()Object.seal() 對私有元素沒有影響。

有關私有欄位如何以及何時初始化的更多資訊,請參閱公共類欄位

示例

私有欄位

私有欄位包括私有例項欄位和私有靜態欄位。私有欄位只能在類宣告內部訪問。

私有例項欄位

與其公共對應物一樣,私有例項欄位

  • 在基類中建構函式執行之前新增,或在子類中呼叫 super() 之後立即新增,並且
  • 僅在類的例項上可用。
js
class ClassWithPrivateField {
  #privateField;

  constructor() {
    this.#privateField = 42;
  }
}

class Subclass extends ClassWithPrivateField {
  #subPrivateField;

  constructor() {
    super();
    this.#subPrivateField = 23;
  }
}

new Subclass(); // In some dev tools, it shows Subclass {#privateField: 42, #subPrivateField: 23}

注意: ClassWithPrivateField 基類中的 #privateFieldClassWithPrivateField 來說是私有的,並且不能從派生類 Subclass 訪問。

返回覆蓋物件

類的建構函式可以返回一個不同的物件,該物件將用作派生類建構函式的新 this。然後派生類可以在該返回物件上定義私有欄位——這意味著可以將私有欄位“標記”到不相關的物件上。

js
class Stamper extends class {
  // A base class whose constructor returns the object it's given
  constructor(obj) {
    return obj;
  }
} {
  // This declaration will "stamp" the private field onto the object
  // returned by the base class constructor
  #stamp = 42;
  static getStamp(obj) {
    return obj.#stamp;
  }
}

const obj = {};
new Stamper(obj);
// `Stamper` calls `Base`, which returns `obj`, so `obj` is
// now the `this` value. `Stamper` then defines `#stamp` on `obj`

console.log(obj); // In some dev tools, it shows {#stamp: 42}
console.log(Stamper.getStamp(obj)); // 42
console.log(obj instanceof Stamper); // false

// You cannot stamp private elements twice
new Stamper(obj); // Error: Initializing an object twice is an error with private fields

警告:這可能是一個非常令人困惑的操作。通常建議你避免從建構函式返回任何東西——尤其是與 this 無關的東西。

私有靜態欄位

與其公共對應物一樣,私有靜態欄位

  • 在類評估時新增到類建構函式中,並且
  • 僅在類本身上可用。
js
class ClassWithPrivateStaticField {
  static #privateStaticField = 42;

  static publicStaticMethod() {
    return ClassWithPrivateStaticField.#privateStaticField;
  }
}

console.log(ClassWithPrivateStaticField.publicStaticMethod()); // 42

私有靜態欄位有一個限制:只有定義私有靜態欄位的類才能訪問該欄位。這在使用 this 時可能導致意外行為。在以下示例中,當我們嘗試呼叫 Subclass.publicStaticMethod() 時,this 指向 Subclass 類(而不是 ClassWithPrivateStaticField 類),因此會導致 TypeError

js
class ClassWithPrivateStaticField {
  static #privateStaticField = 42;

  static publicStaticMethod() {
    return this.#privateStaticField;
  }
}

class Subclass extends ClassWithPrivateStaticField {}

Subclass.publicStaticMethod(); // TypeError: Cannot read private member #privateStaticField from an object whose class did not declare it

如果你用 super 呼叫方法,情況也是如此,因為super 方法不會以超類作為 this 呼叫

js
class ClassWithPrivateStaticField {
  static #privateStaticField = 42;

  static publicStaticMethod() {
    // When invoked through super, `this` still refers to Subclass
    return this.#privateStaticField;
  }
}

class Subclass extends ClassWithPrivateStaticField {
  static callSuperMethod() {
    return super.publicStaticMethod();
  }
}

Subclass.callSuperMethod(); // TypeError: Cannot read private member #privateStaticField from an object whose class did not declare it

建議始終透過類名而不是 this 訪問私有靜態欄位,這樣繼承就不會破壞該方法。

私有方法

私有方法包括私有例項方法和私有靜態方法。私有方法只能在類宣告內部訪問。

私有例項方法

與其公共對應物不同,私有例項方法

  • 在安裝例項欄位之前立即安裝,並且
  • 僅在類的例項上可用,而不在其 .prototype 屬性上可用。
js
class ClassWithPrivateMethod {
  #privateMethod() {
    return 42;
  }

  publicMethod() {
    return this.#privateMethod();
  }
}

const instance = new ClassWithPrivateMethod();
console.log(instance.publicMethod()); // 42

私有例項方法可以是生成器、非同步或非同步生成器函式。私有 getter 和 setter 也可能,並且遵循與其公共gettersetter 對應物相同的語法要求。

js
class ClassWithPrivateAccessor {
  #message;

  get #decoratedMessage() {
    return `🎬${this.#message}🛑`;
  }
  set #decoratedMessage(msg) {
    this.#message = msg;
  }

  constructor() {
    this.#decoratedMessage = "hello world";
    console.log(this.#decoratedMessage);
  }
}

new ClassWithPrivateAccessor(); // 🎬hello world🛑

與公共方法不同,私有方法不能在其類的 .prototype 屬性上訪問。

js
class C {
  #method() {}

  static getMethod(x) {
    return x.#method;
  }
}

console.log(C.getMethod(new C())); // [Function: #method]
console.log(C.getMethod(C.prototype)); // TypeError: Receiver must be an instance of class C

私有靜態方法

與其公共對應物一樣,私有靜態方法

  • 在類評估時新增到類建構函式中,並且
  • 僅在類本身上可用。
js
class ClassWithPrivateStaticMethod {
  static #privateStaticMethod() {
    return 42;
  }

  static publicStaticMethod() {
    return ClassWithPrivateStaticMethod.#privateStaticMethod();
  }
}

console.log(ClassWithPrivateStaticMethod.publicStaticMethod()); // 42

私有靜態方法可以是生成器、非同步和非同步生成器函式。

先前為私有靜態欄位提到的相同限制對私有靜態方法也適用,同樣在使用 this 時可能導致意外行為。在以下示例中,當我們嘗試呼叫 Subclass.publicStaticMethod() 時,this 指向 Subclass 類(而不是 ClassWithPrivateStaticMethod 類),因此會導致 TypeError

js
class ClassWithPrivateStaticMethod {
  static #privateStaticMethod() {
    return 42;
  }

  static publicStaticMethod() {
    return this.#privateStaticMethod();
  }
}

class Subclass extends ClassWithPrivateStaticMethod {}

console.log(Subclass.publicStaticMethod()); // TypeError: Cannot read private member #privateStaticMethod from an object whose class did not declare it

模擬私有建構函式

許多其他語言都包含將建構函式標記為私有的功能,這可以防止在類本身之外例項化該類——你只能使用建立例項的靜態工廠方法,或者根本無法建立例項。JavaScript 沒有原生方法可以做到這一點,但可以透過使用私有靜態標誌來實現。

js
class PrivateConstructor {
  static #isInternalConstructing = false;

  constructor() {
    if (!PrivateConstructor.#isInternalConstructing) {
      throw new TypeError("PrivateConstructor is not constructable");
    }
    PrivateConstructor.#isInternalConstructing = false;
    // More initialization logic
  }

  static create() {
    PrivateConstructor.#isInternalConstructing = true;
    const instance = new PrivateConstructor();
    return instance;
  }
}

new PrivateConstructor(); // TypeError: PrivateConstructor is not constructable
PrivateConstructor.create(); // PrivateConstructor {}

規範

規範
ECMAScript® 2026 語言規範
# prod-PrivateIdentifier
ECMAScript® 2026 語言規範
# prod-00OK517S

瀏覽器相容性

javascript.classes.private_class_fields

javascript.classes.private_class_fields_in

javascript.classes.private_class_methods

另見