公共類欄位

Baseline 已廣泛支援

此功能已成熟,並可在多種裝置和瀏覽器版本上執行。自 2022 年 9 月起,所有瀏覽器都已支援此功能。

公共欄位是定義在每個類例項或類建構函式上的可寫、可列舉、可配置的屬性。

語法

js
class ClassWithField {
  instanceField;
  instanceFieldWithInitializer = "instance field";
  static staticField;
  static staticFieldWithInitializer = "static field";
}

還有一些額外的語法限制

  • 靜態屬性(欄位或方法)的名稱不能是 prototype
  • 類欄位(靜態或例項)的名稱不能是 constructor

描述

本頁面詳細介紹了公共例項欄位。

公共例項欄位存在於類的每個已建立例項上。透過宣告公共欄位,可以確保該欄位始終存在,並且類定義更具自文件性。

公共例項欄位在基類構造時(在建構函式體執行之前)新增到例項中,或在子類中 super() 返回之後立即新增。沒有初始化的欄位被初始化為 undefined。像屬性一樣,欄位名可以是計算得出的。

js
const PREFIX = "prefix";

class ClassWithField {
  field;
  fieldWithInitializer = "instance field";
  [`${PREFIX}Field`] = "prefixed field";
}

const instance = new ClassWithField();
console.log(Object.hasOwn(instance, "field")); // true
console.log(instance.field); // undefined
console.log(instance.fieldWithInitializer); // "instance field"
console.log(instance.prefixField); // "prefixed field"

計算得出的欄位名只在類定義時評估一次。這意味著每個類總是有一組固定的欄位名,兩個例項不能透過計算得出的名稱擁有不同的欄位名。計算表示式中的 this 值是圍繞類定義的 this,並且引用類的名稱會引發 ReferenceError,因為該類尚未初始化。awaityield 在此表示式中按預期工作。

js
class C {
  [Math.random()] = 1;
}

console.log(new C());
console.log(new C());
// Both instances have the same field name

在欄位初始化器中,this 指的是正在構建的類例項,而 super 指的是基類的 prototype 屬性,其中包含基類的例項方法,但不包含其例項欄位。

js
class Base {
  baseField = "base field";
  anotherBaseField = this.baseField;
  baseMethod() {
    return "base method output";
  }
}

class Derived extends Base {
  subField = super.baseMethod();
}

const base = new Base();
const sub = new Derived();

console.log(base.anotherBaseField); // "base field"

console.log(sub.subField); // "base method output"

每次建立新例項時都會評估欄位初始化器表示式。(因為每個例項的 this 值都不同,所以初始化器表示式可以訪問例項特定的屬性。)

js
class C {
  obj = {};
}

const instance1 = new C();
const instance2 = new C();
console.log(instance1.obj === instance2.obj); // false

表示式是同步評估的。您不能在初始化器表示式中使用 awaityield。(可以將初始化器表示式看作被隱式包裝在一個函式中。)

由於類的例項欄位在各自的建構函式執行之前新增,因此您可以在建構函式中訪問欄位的值。但是,由於派生類的例項欄位在 super() 返回之後定義,因此基類的建構函式無法訪問派生類的欄位。

js
class Base {
  constructor() {
    console.log("Base constructor:", this.field);
  }
}

class Derived extends Base {
  field = 1;
  constructor() {
    super();
    console.log("Derived constructor:", this.field);
    this.field = 2;
  }
}

const instance = new Derived();
// Base constructor: undefined
// Derived constructor: 1
console.log(instance.field); // 2

欄位逐個新增。欄位初始化器可以引用其上方的欄位值,但不能引用其下方的欄位值。所有例項和靜態方法都預先新增,並且可以訪問,儘管如果它們引用下方正在初始化的欄位,則呼叫它們可能不會按預期執行。

js
class C {
  a = 1;
  b = this.c;
  c = this.a + 1;
  d = this.c + 1;
}

const instance = new C();
console.log(instance.d); // 3
console.log(instance.b); // undefined

注意: 這對於私有欄位更為重要,因為即使私有欄位在下方宣告,訪問未初始化的私有欄位也會丟擲 TypeError。(如果未宣告私有欄位,則會引發早期 SyntaxError。)

由於類欄位是使用 [[DefineOwnProperty]] 語義(本質上是 Object.defineProperty())新增的,因此派生類中的欄位宣告不會呼叫基類中的 setter。此行為與在建構函式中使用 this.field = … 不同。

js
class Base {
  set field(val) {
    console.log(val);
  }
}

class DerivedWithField extends Base {
  field = 1;
}

const instance = new DerivedWithField(); // No log

class DerivedWithConstructor extends Base {
  constructor() {
    super();
    this.field = 1;
  }
}

const instance2 = new DerivedWithConstructor(); // Logs 1

注意: 在類欄位規範使用 [[DefineOwnProperty]] 語義最終確定之前,大多數轉譯器,包括 Babeltsc,將類欄位轉換為 DerivedWithConstructor 形式,這在類欄位標準化後導致了細微的錯誤。

示例

使用類欄位

類欄位不能依賴於建構函式的引數,因此欄位初始化器通常為每個例項評估為相同的值(除非相同的表示式每次都可以評估為不同的值,例如 Math.random() 或物件初始化器)。

js
class Person {
  name = nameArg; // nameArg is out of scope of the constructor
  constructor(nameArg) {}
}
js
class Person {
  // All instances of Person will have the same name
  name = "Dragomir";
}

然而,即使宣告一個空的類欄位也是有益的,因為它表明了該欄位的存在,這允許型別檢查器和人工讀者靜態分析類的形狀。

js
class Person {
  name;
  age;
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

上面的程式碼看起來是重複的,但考慮 this 動態變異的情況:顯式欄位宣告清楚地表明瞭哪些欄位肯定會出現在例項上。

js
class Person {
  name;
  age;
  constructor(properties) {
    Object.assign(this, properties);
  }
}

由於初始化器在基類執行後才評估,您可以訪問基類建構函式建立的屬性。

js
class Person {
  name;
  age;
  constructor(name, age) {
    this.name = name;
    this.age = age;
  }
}

class Professor extends Person {
  name = `Professor ${this.name}`;
}

console.log(new Professor("Radev", 54).name); // "Professor Radev"

規範

規範
ECMAScript® 2026 語言規範
# prod-FieldDefinition

瀏覽器相容性

另見