类
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2017.
类是创建对象的模板。他们用代码封装数据以处理该数据。JS 中的类基于 prototypes 构建,但也具有一些类特有的语法和语义。
¥Classes are a template for creating objects. They encapsulate data with code to work on that data. Classes in JS are built on prototypes but also have some syntax and semantics that are unique to classes.
有关更多示例和说明,请参阅 使用类 指南。
¥For more examples and explanations, see the Using classes guide.
描述
定义类
¥Defining classes
类实际上是“特殊的 functions”,就像定义 函数表达式 和 函数声明 一样,类可以通过两种方式定义:类表达式 或 类声明。
¥Classes are in fact "special functions", and just as you can define function expressions and function declarations, a class can be defined in two ways: a class expression or a class declaration.
// 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;
}
};
与函数表达式一样,类表达式可以是匿名的,或者具有与其分配给的变量不同的名称。但是,与函数声明不同,类声明具有与 let
或 const
相同的 颞死区 限制,并且其行为就像 未提升 一样。
¥Like function expressions, class expressions may be anonymous, or have a name that's different from the variable that it's assigned to. However, unlike function declarations, class declarations have the same temporal dead zone restrictions as let
or const
and behave as if they are not hoisted.
类体
¥Class body
类的主体是大括号 {}
中的部分。你可以在此处定义类成员,例如方法或构造函数。
¥The body of a class is the part that is in curly braces {}
. This is where you define class members, such as methods or constructor.
即使没有 "use strict"
指令,类的主体也会在 严格模式 中执行。
¥The body of a class is executed in strict mode even without the "use strict"
directive.
类元素可以由三个方面来表征:
¥A class element can be characterized by three aspects:
- 种类:Getter、setter、方法或字段
- 地点:静态或实例
- 能见度:公立或私立
它们加起来总共有 16 种可能的组合。为了更逻辑地划分参考文献并避免内容重叠,不同的元素在不同的页面中详细介绍:
¥Together, they add up to 16 possible combinations. To divide the reference more logically and avoid overlapping content, the different elements are introduced in detail in different pages:
- 方法定义
-
公共实例方法
- getter
-
公共实例 getter
- setter
-
公共实例设置器
- 公共类字段
-
公共实例字段
static
-
公共静态方法、getter、setter 和字段
- 私有属性
-
一切都是私有的
注意:私有属性有一个限制,即同一类中声明的所有属性名称必须是唯一的。所有其他公共属性都没有此限制 - 你可以拥有多个同名的公共属性,最后一个属性会覆盖其他属性。这与 对象初始值设定项 中的行为相同。
¥Note: Private properties have the restriction that all property names declared in the same class must be unique. All other public properties do not have this restriction — you can have multiple public properties with the same name, and the last one overwrites the others. This is the same behavior as in object initializers.
此外,还有两种特殊的类元素语法:constructor
和 静态初始化块,有自己的参考文献。
¥In addition, there are two special class element syntaxes: constructor
and static initialization blocks, with their own references.
构造函数
¥Constructor
constructor
方法是用于创建和初始化用类创建的对象的特殊方法。一个类中只能有一个名为 "构造函数" 的特殊方法 - 如果该类包含多次出现的 constructor
方法,则会抛出 SyntaxError
异常。
¥The constructor
method is a special method for creating and initializing an object created with a class. There can only be one special method with the name "constructor" in a class — a SyntaxError
is thrown if the class contains more than one occurrence of a constructor
method.
构造函数可以使用 super
关键字来调用超类的构造函数。
¥A constructor can use the super
keyword to call the constructor of the super class.
你可以在构造函数内创建实例属性:
¥You can create instance properties inside the constructor:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
或者,如果实例属性的值不依赖于构造函数的参数,则可以将它们定义为 类字段。
¥Alternatively, if your instance properties' values do not depend on the constructor's arguments, you can define them as class fields.
静态初始化块
¥Static initialization blocks
静态初始化块 允许对 静态属性 进行灵活的初始化,包括在初始化期间评估语句,同时授予对私有范围的访问权限。
¥Static initialization blocks allow flexible initialization of static properties, including the evaluation of statements during initialization, while granting access to the private scope.
可以声明多个静态块,并且这些块可以与静态字段和方法的声明交错(所有静态项都按声明顺序求值)。
¥Multiple static blocks can be declared, and these can be interleaved with the declaration of static fields and methods (all static items are evaluated in declaration order).
方法
¥Methods
方法在每个类实例的原型上定义,并由所有实例共享。方法可以是普通函数、异步函数、生成器函数或异步生成器函数。欲了解更多信息,请参阅 方法定义。
¥Methods are defined on the prototype of each class instance and are shared by all instances. Methods can be plain functions, async functions, generator functions, or async generator functions. For more information, see method definitions.
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 methods and fields
static
关键字定义类的静态方法或字段。静态属性(字段和方法)是在类本身而不是每个实例上定义的。静态方法通常用于为应用创建实用程序函数,而静态字段对于缓存、固定配置或不需要跨实例复制的任何其他数据很有用。
¥The static
keyword defines a static method or field for a class. Static properties (fields and methods) are defined on the class itself instead of each instance. Static methods are often used to create utility functions for an application, whereas static fields are useful for caches, fixed-configuration, or any other data that doesn't need to be replicated across instances.
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
现场声明
¥Field declarations
使用类字段声明语法,构造函数 示例可以写为:
¥With the class field declaration syntax, the constructor example can be written as:
class Rectangle {
height = 0;
width;
constructor(height, width) {
this.height = height;
this.width = width;
}
}
类字段类似于对象属性,而不是变量,因此我们不使用 const
等关键字来声明它们。在 JavaScript 中,私有属性 使用特殊的标识符语法,因此也不应该使用 public
和 private
等修饰符关键字。
¥Class fields are similar to object properties, not variables, so we don't use keywords such as const
to declare them. In JavaScript, private properties use a special identifier syntax, so modifier keywords like public
and private
should not be used either.
如上所示,可以使用或不使用默认值来声明字段。没有默认值的字段默认为 undefined
。通过预先声明字段,类定义变得更加自文档化,并且字段始终存在,这有助于优化。
¥As seen above, the fields can be declared with or without a default value. Fields without default values default to undefined
. By declaring fields up-front, class definitions become more self-documenting, and the fields are always present, which help with optimizations.
请参阅 公共类字段 了解更多信息。
¥See public class fields for more information.
私有属性
¥Private properties
使用私有字段,可以将定义细化如下。
¥Using private fields, the definition can be refined as below.
class Rectangle {
#height = 0;
#width;
constructor(height, width) {
this.#height = height;
this.#width = width;
}
}
从类外部引用私有字段是错误的;它们只能在类体内读取或写入。通过定义类外部不可见的内容,你可以确保类的用户不能依赖于内部结构,而内部结构可能会因版本而异。
¥It's an error to reference private fields from outside of the class; they can only be read or written within the class body. By defining things that are not visible outside of the class, you ensure that your classes' users can't depend on internals, which may change from version to version.
私有字段只能在字段声明中预先声明。它们不能像普通属性那样通过分配来创建。
¥Private fields can only be declared up-front in a field declaration. They cannot be created later through assigning to them, the way that normal properties can.
欲了解更多信息,请参阅 私有属性。
¥For more information, see private properties.
遗产
¥Inheritance
extends
关键字用于类声明或类表达式中,以将类创建为另一个构造函数(类或函数)的子级。
¥The extends
keyword is used in class declarations or class expressions to create a class as a child of another constructor (either a class or a function).
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()
,然后再使用 this
。super
关键字也可以用来调用超类相应的方法。
¥If there is a constructor present in the subclass, it needs to first call super()
before using this
. The super
keyword can also be used to call corresponding methods of super class.
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.
评估顺序
¥Evaluation order
当评估 class
声明 或 class
表达 时,其各个组件按以下顺序评估:
¥When a class
declaration or class
expression is evaluated, its various components are evaluated in the following order:
- 首先评估
extends
子句(如果存在)。它必须计算为有效的构造函数或null
,否则会抛出TypeError
。 - 如果
constructor
不存在,则提取constructor
方法,并用默认实现替换。但是,由于constructor
定义只是一个方法定义,因此这一步是不可观察的。 - 类元素的属性键按照声明的顺序进行计算。如果计算属性键,则计算计算表达式,并将
this
值设置为类周围的this
值(而不是类本身)。尚未评估任何属性值。 - 方法和访问器按照声明的顺序安装。实例方法和访问器安装在当前类的
prototype
属性上,静态方法和访问器安装在类本身上。私有实例方法和访问器被保存以便稍后直接安装在实例上。这一步是不可观察的。 - 该类现在使用
extends
指定的原型和constructor
指定的实现进行初始化。对于上述所有步骤,如果计算表达式尝试访问类的名称,则会抛出ReferenceError
,因为该类尚未初始化。 - 类元素的值按照声明的顺序进行计算:
- 该类现已完全初始化,可以用作构造函数。
有关如何创建实例的信息,请参阅 constructor
参考。
¥For how instances are created, see the constructor
reference.
示例
将 this 与实例和静态方法绑定
¥Binding this with instance and static methods
当调用没有 this
值的静态或实例方法时,例如将方法分配给变量然后调用它,则 this
值将是方法内的 undefined
。即使不存在 "use strict"
指令,此行为也是相同的,因为 class
主体中的代码始终以严格模式执行。
¥When a static or instance method is called without a value for this
, such as by assigning the method to a variable and then calling it, the this
value will be undefined
inside the method. This behavior is the same even if the "use strict"
directive isn't present, because code within the class
body is always executed in strict mode.
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
。
¥If we rewrite the above using traditional function-based syntax in non–strict mode, then this
method calls are automatically bound to globalThis
. In strict mode, the value of this
remains as undefined
.
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)
规范
Specification |
---|
ECMAScript Language Specification # sec-class-definitions |
浏览器兼容性
BCD tables only load in the browser
也可以看看
¥See also
- 使用类 指南
class
class
表达- 函数
- hacks.mozilla.org 上的 ES6 深入了解:类 (2015)