A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Classes/Public_class_fields below:

å…¬æœ‰ç±»å—æ®µ - JavaScript | MDN

本页面将详细介绍公共实例字段。

公有实例字段存在于类的每个已创建实例中。通过声明公共字段,可以确保该字段始终存在,而且类的定义也更加自文档化(self-documenting)。

公共实例字段会在基类的构造时(构造函数主体运行之前)或子类的 super() 返回后添加到实例中。没有初始化器的字段会被初始化为 undefined。与属性一样,字段名称也可以计算。

const PREFIX = "prefix";

class ClassWithField {
  field;
  fieldWithInitializer = "实例字段";
  [`${PREFIX}Field`] = "带前缀字段";
}

const instance = new ClassWithField();
console.log(Object.hasOwn(instance, "field")); // true
console.log(instance.field); // undefined
console.log(instance.fieldWithInitializer); // "实例字段"
console.log(instance.prefixField); // "带前缀字段"

只在类定义时对计算字段名进行一次求值。这意味着每个类总是有一组固定的字段名,两个实例不能通过计算的名称拥有不同的字段名。计算表达式中的 this 指向类声明所处上下文的 this,而引用类名会导致 ReferenceError,因为类尚未初始化。在此表达式中,await 和 yield 按预期工作。

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

console.log(new C());
console.log(new C());
// 两个实例拥有相同的字段名称

在字段初始化器中,this 指向正在构建的类实例,而 super 指向基类的 prototype 属性,它包含基类的实例方法,但不包含其实例字段。

class Base {
  baseField = "基类字段";
  anotherBaseField = this.baseField;
  baseMethod() {
    return "基类方法输出";
  }
}

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

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

console.log(base.anotherBaseField); // "基类字段"

console.log(sub.subField); // "基类方法输出"

每次创建新实例时,都会对字段初始化表达式进行求值。(因为每个实例的 this 值都不同,所以初始化表达式可以访问特定于实例的属性)。

class C {
  obj = {};
}

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

表达式是同步求值的。不能在初始化表达式中使用 await 或 yield。(将初始化表达式视为被隐式封装在函数中。)

由于类的实例字段是在各自的构造函数运行之前添加的,因此可以在构造函数中访问字段的值。然而,由于派生类的实例字段是在 super() 返回后定义的,因此基类的构造函数无法访问派生类的字段。

class Base {
  constructor() {
    console.log("基类的构造函数:", this.field);
  }
}

class Derived extends Base {
  field = 1;
  constructor() {
    super();
    console.log("派生类的控制函数:", this.field);
    this.field = 2;
  }
}

const instance = new Derived();
// 基类的构造函数:undefined
// 派生类的控制函数:1
console.log(instance.field); // 2

字段是逐个添加的。字段初始化器可以引用它上面的字段值,但不能引用它下面的字段值。所有实例方法和静态方法都会事先添加并可以访问,但如果这些方法引用的字段低于正在初始化的字段,那么调用这些方法时可能会出现与预期不符的情况。

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 = …。

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(); // 打印 1

备注: 在类字段规范最终确定为[[DefineOwnProperty]]语义之前,包括 Babel 和 tsc在内的大多数转译器都将类字段转换为 DerivedWithConstructor 形式,这在类字段规范化之后造成了一些微妙的错误。


RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4