类的主体是放在大括号中的部分{}
。在这里定义类成员,例如方法或构造函数。
类的主体以严格模式执行,即,此处编写的代码将采用更严格的语法以提高性能,否则将抛出一些其他的静默错误,并且某些关键字保留给将来的ECMAScript版本。
该constructor
方法是用于创建和初始化使用创建的对象的特殊方法class
。在一个类中,只能有一个名为“ constructor”的特殊方法。SyntaxError
如果该类包含一个以上的constructor
方法实例,则将引发A。
构造函数可以使用super
关键字来调用超类的构造函数。
另请参见方法定义。
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// Method
calcArea() {
return this.height * this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100
的static
关键字定义为一类的静态方法。静态方法被称为没有实例化的类并不能通过类实例调用。静态方法通常用于为应用程序创建实用程序功能。
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
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.distance; //undefined
p2.distance; //undefined
console.log(Point.distance(p1, p2)); // 7.0710678118654755
this
与原型和静态方法绑定当调用静态或原型方法而没有for的值时 this
,例如通过将变量分配给该方法然后调用它,则该this
值将undefined
在方法内部。即使"use strict"
指令不存在,此行为也将是相同的,因为class
主体的语法边界内的代码始终以严格模式执行。
class Animal {
speak() {
return this;
}
static eat() {
return this;
}
}
let obj = new Animal();
obj.speak(); // the Animal object
let speak = obj.speak;
speak(); // undefined
Animal.eat() // class Animal
let eat = Animal.eat;
eat(); // undefined
如果我们在非严格模式下使用传统的基于函数的语法重写以上内容,则this
方法调用将自动绑定到初始this
值,默认情况下,该初始值为全局对象。在严格模式下,将不会发生自动绑定。this
保持不变的价值。
function Animal() { }
Animal.prototype.speak = function() {
return this;
}
Animal.eat = function() {
return this;
}
let obj = new Animal();
let speak = obj.speak;
speak(); // global object (in non–strict mode)
let eat = Animal.eat;
eat(); // global object (in non-strict mode)
实例属性必须在类方法内定义:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
静态(类方面)数据属性和原型数据属性必须在ClassBody声明之外定义:
Rectangle.staticWidth = 20;
Rectangle.prototype.prototypeWidth = 25;
公共和私有字段声明是JavaScript标准委员会TC39提出的实验性功能(第3阶段)。对浏览器的支持是有限的,但是可以通过构建步骤(如Babel)使用该功能。
使用JavaScript字段声明语法,上面的示例可以写成:
class Rectangle {
height = 0;
width;
constructor(height, width) {
this.height = height;
this.width = width;
}
}
通过预先声明字段,类定义变得更加自我记录,并且字段始终存在。
如上所示,可以使用默认值或不使用默认值来声明字段。
有关更多信息,请参见公共类字段。
使用私有字段,可以按以下方式细化定义。
class Rectangle {
#height = 0;
#width;
constructor(height, width) {
this.#height = height;
this.#width = width;
}
}
从类外部引用私有字段是错误的。它们只能在类主体中读取或写入。通过定义在类外部不可见的内容,可以确保类的用户不会依赖于内部,这可能会导致版本之间的差异。
私有字段只能在字段声明中预先声明。
以后无法通过分配普通属性的方式来分配专用字段来创建专用字段。
有关更多信息,请参见私有类字段。
extends
该extends
关键字用于类声明或类表达式中,以将一个类创建为另一个类的子级。
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.`);
}
}
let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
如果子类中存在构造函数,则需要先调用super(),然后再使用“ this”。
也可以扩展传统的基于功能的“类”:
function Animal (name) {
this.name = name;
}
Animal.prototype.speak = function () {
console.log(`${this.name} makes a noise.`);
}
class Dog extends Animal {
speak() {
console.log(`${this.name} barks.`);
}
}
let d = new Dog('Mitzie');
d.speak(); // Mitzie barks.
// For similar methods, the child's method takes precedence over parent's method
请注意,类不能扩展常规(不可构造)对象。如果要从常规对象继承,则可以使用Object.setPrototypeOf()
:
const Animal = {
speak() {
console.log(`${this.name} makes a noise.`);
}
};
class Dog {
constructor(name) {
this.name = name;
}
}
// If you do not do this you will get a TypeError when you invoke speak
Object.setPrototypeOf(Dog.prototype, Animal);
let d = new Dog('Mitzie');
d.speak(); // Mitzie makes a noise.
您可能要返回Array
派生数组类中的对象MyArray
。种类模式可让您覆盖默认构造函数。
例如,当使用诸如这样的方法map()
返回默认构造函数时,您希望这些方法返回父Array
对象而不是MyArray
对象。该Symbol.species
符号使您可以执行此操作:
class MyArray extends Array {
// Overwrite species to the parent Array constructor
static get [Symbol.species]() { return Array; }
}
let a = new MyArray(1,2,3);
let mapped = a.map(x => x * x);
console.log(mapped instanceof MyArray); // false
console.log(mapped instanceof Array); // true
super
该super
关键字用来调用父类的相应方法。与基于原型的继承相比,这是一个优势。
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.`);
}
}
let l = new Lion('Fuzzy');
l.speak();
// Fuzzy makes a noise.
// Fuzzy roars.
抽象子类或混入是类的模板。ECMAScript类只能有一个超类,因此,例如,无法从工具类进行多重继承。该功能必须由超类提供。
具有超类作为输入和扩展该超类作为输出的子类的函数可用于在ECMAScript中实现混入:
let calculatorMixin = Base => class extends Base {
calc() { }
};
let randomizerMixin = Base => class extends Base {
randomize() { }
};
然后,可以使用以下方式编写使用这些混入的类:
class Foo { }
class Bar extends calculatorMixin(randomizerMixin(Foo)) { }