Baseline 已广泛支持

此特性已经非常成熟,并且适用于许多设备和浏览器版本。自 2016 年 3 月以来,它已在所有浏览器中可用。

类是用于创建对象的模板。它们将数据与处理该数据的代码封装在一起。JS 中的类构建于原型之上,但也具有一些类特有的语法和语义。

有关更多示例和解释,请参阅使用类指南。

描述

定义类

类实际上是“特殊的函数”,就像你可以定义函数表达式函数声明一样,类可以通过两种方式定义:类表达式类声明

js
// Declaration
class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

// Expression; the class is anonymous but assigned to a variable
const Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

// Expression; the class has its own name
const Rectangle = class Rectangle2 {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

与函数表达式类似,类表达式可以是匿名的,或者拥有一个不同于其赋值变量的名称。然而,与函数声明不同,类声明与 letconst 具有相同的暂时死区限制,并且行为表现为不会被提升

类体

类的身体是花括号 {} 中的部分。你可以在这里定义类成员,例如方法或构造函数。

即使没有 "use strict" 指令,类体也会在严格模式下执行。

类元素可以从三个方面进行描述:

  • 种类:Getter、setter、方法或字段
  • 位置:静态或实例
  • 可见性:公共或私有

它们共同构成了 16 种可能的组合。为了更逻辑地划分参考资料并避免内容重叠,不同的元素在不同的页面中详细介绍。

方法定义

公共实例方法

getter

公共实例 getter

setter

公共实例 setter

公共类字段

公共实例字段

static

公共静态方法、getter、setter 和字段

私有元素

所有私有元素

注意:私有元素有一个限制,即在同一个类中声明的所有私有名称都必须是唯一的。所有其他公共属性没有这个限制——你可以有多个同名的公共属性,最后一个会覆盖其他的。这与对象初始化器中的行为相同。

此外,还有两种特殊的类元素语法:constructor静态初始化块,它们有各自的引用。

构造函数

constructor 方法是用于创建和初始化类创建的对象的特殊方法。在一个类中只能有一个名为“constructor”的特殊方法——如果类中包含多个 constructor 方法,则会抛出 SyntaxError

构造函数可以使用 super 关键字调用超类的构造函数。

你可以在构造函数中创建实例属性

js
class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

或者,如果你的实例属性值不依赖于构造函数的参数,你可以将它们定义为类字段

静态初始化块

静态初始化块允许对静态属性进行灵活初始化,包括在初始化期间评估语句,同时授予访问私有作用域的权限。

可以声明多个静态块,并且这些块可以与静态字段和方法的声明交错(所有静态项都按声明顺序进行评估)。

方法

方法在每个类实例的原型上定义,并由所有实例共享。方法可以是普通函数、异步函数、生成器函数或异步生成器函数。有关更多信息,请参阅方法定义

js
class Rectangle {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
  // Getter
  get area() {
    return this.calcArea();
  }
  // Method
  calcArea() {
    return this.height * this.width;
  }
  *getSides() {
    yield this.height;
    yield this.width;
    yield this.height;
    yield this.width;
  }
}

const square = new Rectangle(10, 10);

console.log(square.area); // 100
console.log([...square.getSides()]); // [10, 10, 10, 10]

静态方法和字段

static 关键字为类定义静态方法或字段。静态属性(字段和方法)定义在类本身上,而不是每个实例上。静态方法通常用于为应用程序创建实用函数,而静态字段则适用于缓存、固定配置或任何不需要在实例之间复制的数据。

js
class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }

  static displayName = "Point";
  static distance(a, b) {
    const dx = a.x - b.x;
    const dy = a.y - b.y;

    return Math.hypot(dx, dy);
  }
}

const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName; // undefined
p1.distance; // undefined
p2.displayName; // undefined
p2.distance; // undefined

console.log(Point.displayName); // "Point"
console.log(Point.distance(p1, p2)); // 7.0710678118654755

字段声明

使用类字段声明语法,构造函数的示例可以写成:

js
class Rectangle {
  height = 0;
  width;
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
}

类字段类似于对象属性,而不是变量,因此我们不使用诸如 const 等关键字来声明它们。在 JavaScript 中,私有元素使用特殊的标识符语法,因此也不应使用 publicprivate 等修饰符关键字。

如上所示,字段可以带或不带默认值声明。没有默认值的字段默认为 undefined。通过提前声明字段,类定义变得更具自解释性,并且字段始终存在,这有助于优化。

有关更多信息,请参阅公共类字段

私有元素

使用私有字段,定义可以如下完善。

js
class Rectangle {
  #height = 0;
  #width;
  constructor(height, width) {
    this.#height = height;
    this.#width = width;
  }
}

从类外部引用私有字段是错误的;它们只能在类体内部读取或写入。通过定义类外部不可见的内容,你可以确保类的用户不会依赖于内部实现,这些内部实现可能会因版本而异。

私有字段只能在字段声明中提前声明。它们不能像普通属性那样通过赋值稍后创建。

私有方法和访问器也可以使用与其公共对应项相同的语法定义,但标识符以 # 开头。

有关更多信息,请参阅私有元素

继承

extends 关键字用于类声明类表达式,以创建作为另一个构造函数(类或函数)子类的类。

js
class Animal {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // call the super class constructor and pass in the name parameter
  }

  speak() {
    console.log(`${this.name} barks.`);
  }
}

const d = new Dog("Mitzie");
d.speak(); // Mitzie barks.

如果子类中存在构造函数,它需要先调用 super(),然后才能使用 thissuper 关键字也可以用于调用超类的相应方法。

js
class Cat {
  constructor(name) {
    this.name = name;
  }

  speak() {
    console.log(`${this.name} makes a noise.`);
  }
}

class Lion extends Cat {
  speak() {
    super.speak();
    console.log(`${this.name} roars.`);
  }
}

const l = new Lion("Fuzzy");
l.speak();
// Fuzzy makes a noise.
// Fuzzy roars.

评估顺序

class 声明class 表达式被评估时,其各个组件按照以下顺序进行评估:

  1. 如果存在 extends 子句,则首先进行评估。它必须评估为一个有效的构造函数或 null,否则会抛出 TypeError
  2. constructor 方法被提取,如果 constructor 不存在,则用默认实现替换。然而,由于 constructor 定义只是一个方法定义,此步骤不可观察。
  3. 类元素的属性键按声明顺序进行评估。如果属性键是计算得出的,则计算表达式将被评估,其 this 值设置为围绕该类的 this 值(而不是类本身)。此时所有属性值都尚未被评估。
  4. 方法和访问器按声明顺序安装。实例方法和访问器安装在当前类的 prototype 属性上,静态方法和访问器安装在类本身上。私有实例方法和访问器被保存以供稍后直接安装在实例上。此步骤不可观察。
  5. 类现在已使用 extends 指定的原型和 constructor 指定的实现进行初始化。对于以上所有步骤,如果一个评估的表达式试图访问类的名称,则会抛出 ReferenceError,因为类尚未初始化。
  6. 类元素的值按声明顺序进行评估
    • 对于每个实例字段(公共或私有),其初始化表达式被保存。初始化器在实例创建期间,在构造函数开始时(对于基类)或在 super() 调用返回之前(对于派生类)立即进行评估。
    • 对于每个静态字段(公共或私有),其初始化器在 this 设置为类本身的情况下进行评估,并且该属性在类上创建。
    • 静态初始化块this 设置为类本身的情况下进行评估。
  7. 类现在已完全初始化,可以作为构造函数使用。

有关实例如何创建,请参阅constructor参考。

示例

使用实例和静态方法绑定 this

当静态方法或实例方法在没有 this 值的情况下被调用时,例如通过将方法赋值给一个变量然后调用它,方法内部的 this 值将是 undefined。即使没有 "use strict" 指令,这种行为也是相同的,因为 class 主体中的代码总是以严格模式执行。

js
class Animal {
  speak() {
    return this;
  }
  static eat() {
    return this;
  }
}

const obj = new Animal();
obj.speak(); // the Animal object
const speak = obj.speak;
speak(); // undefined

Animal.eat(); // class Animal
const eat = Animal.eat;
eat(); // undefined

如果我们使用传统的基于函数的语法在非严格模式下重写上述内容,那么 this 方法调用会自动绑定到 globalThis。在严格模式下,this 的值保持为 undefined

js
function Animal() {}

Animal.prototype.speak = function () {
  return this;
};

Animal.eat = function () {
  return this;
};

const obj = new Animal();
const speak = obj.speak;
speak(); // global object (in non–strict mode)

const eat = Animal.eat;
eat(); // global object (in non-strict mode)

规范

规范
ECMAScript® 2026 语言规范
# sec-class-definitions

浏览器兼容性

另见