Baseline Widely available *

This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2016.

* Some parts of this feature may have varying levels of support.

类是用于创建对象的模板。它们用代码封装数据以对其进行处理。JS 中的类建立在原型之上,同时还具有一些类独有的语法和语义。

更多的示例和释义,参见使用类教程。

描述

定义类

类实际上是“特殊的函数”,就像你能够定义的函数表达式函数声明一样,类也有两种定义方式:类表达式类声明

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

// 类表达式;类是匿名的,但是它被赋值给了变量
const Rectangle = class {
  constructor(height, width) {
    this.height = height;
    this.width = width;
  }
};

// 类表达式;类有它自己的名字
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();
  }
  // 方法
  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} 发出叫声。`);
  }
}

class Dog extends Animal {
  constructor(name) {
    super(name); // 调用父类的构造函数并且传递了 name 参数
  }

  speak() {
    console.log(`${this.name} 吠叫。`);
  }
}

const d = new Dog("Mitzie");
d.speak(); // Mitzie 吠叫。

如果子类中定义了构造函数,那么它必须先调用 super() 才能使用 thissuper 关键字也可以用来调用父类中对应的方法。

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

  speak() {
    console.log(`${this.name} 发出叫声。`);
  }
}

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

const l = new Lion("Fuzzy");
l.speak();
// Fuzzy 发出叫声。
// Fuzzy 吼叫。

求值顺序

当一个 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(); // Animal 对象
const speak = obj.speak;
speak(); // undefined

Animal.eat(); // 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(); // 全局对象(在非严格模式下)

const eat = Animal.eat;
eat(); // 全局对象(在非严格模式下)

规范

Specification
ECMAScript® 2025 Language Specification
# sec-class-definitions

浏览器兼容性

Report problems with this compatibility data on GitHub
desktopmobileserver
Chrome
Edge
Firefox
Opera
Safari
Chrome Android
Firefox for Android
Opera Android
Safari on iOS
Samsung Internet
WebView Android
WebView on iOS
Deno
Node.js
classes
constructor
extends
Private class fields
Private class fields 'in'
Private class methods
Public class fields
static
Static class fields
Class static initialization blocks

Legend

Tip: you can click/tap on a cell for more information.

Full support
Full support
Partial support
Partial support
Has more compatibility info.

参见