JavaScript的类

原文:Classes in JavaScript

从ES6/2015开始,JavaScript新增了面向对象编程的内置关键字class。本文介绍了它的工作原理。

在面向对象编程中,是创建对象的模板。JavaScript的class关键字可以声明一个新的类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}

const obj = new Rectangle(3, 5);
obj.height; // 3
obj.width; // 5

// `instanceof` 关键字检查一个对象是否由某个类创建
obj instanceof Rectangle; // true
({}) instanceof Rectangle; // false

方法

方法就是定义在类中的函数,JavaScript会将方法添加到每个类的实例中。例如,要计算Rectangle的面积,可以定义一个area()方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}

// 要定义一个 `methodName` 方法,在类声明中加入 `methodName() {}`
area() {
return this.width * this.height;
}
}

const obj = new Rectangle(3, 5);
obj.area(); // 15

在这个方法中,this关键字指向了方法所属的类的实例。以上代码中的this指向obj

Statics
A static is a a function that is defined on the class itself. In JavaScript, a class is just another variable, so you can call static functions on a class.

Static

static关键字代表函数是定义到类本身的。JavaScript中,一个类也是一个变量,可以直接调用类的静态函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}

// 要定义一个静态函数 `functionName`在类声明中加入 `static functionName() {}`
static createSquare(length) {
return new Rectangle(length, length);
}
}

const obj = Rectangle.createSquare(5);
obj.height; // 5
obj.width; // 5

getter/setter

定义Rectanglearea,可以用另一种方法——getter。使用getter,不使用方法,就能创建一个动态计算的Rectanglearea属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}

// 要定义一个getter `getterName`在类声明中加入 `get getterName() {}`。注意:getter是函数!
get area() {
return this.height * this.width;
}
}

const obj = new Rectangle(3, 5);
obj.area; // 15

同样,也可以定义一个setter,在设置某个属性时进行调用。例如,需要确定heightwidth都是数值型,可以定义setter,在设置为非数值型时抛出异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}

get height() {
return this._height;
}

set height(v) {
assert.ok(typeof v === 'number', 'height必须为数值型');
return v;
}

get width() {
return this._width;
}

set width(v) {
assert.ok(typeof v === 'number', 'width必须为数值型');
return v;
}
}

const obj = new Rectangle(3, 5);
// 抛出 'AssertionError: height必须为数值型'
obj.height = 'Not a number';

继承

一个类继承另一个类,意味着子类默认具有所有父类的静态方法、方法、gettersetter。而子类可以定义额外的静态方法、方法、gettersetter,子类还可以覆盖基类的静态方法、方法、gettersetter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}

area() {
return this.width * this.height;
}
}

class Square extends Rectangle {
constructor(length) {
// `super` 指向基类的 constructor
super(length, length);
}

// 内切圆半径
// http://mathworld.wolfram.com/Square.html
inradius() {
return this.height / 2;
}
}

const obj = new Square(5);

obj.area(); // 25, 来源于 `Rectangle`基类
obj.inradius(); // 2.5, 来源于 `Square` 类

obj instanceof Square; // true
obj instanceof Rectangle; // true

继承仍然是基于原型的

关键字extends在底层仍然使用基于原型的继承。也就是说,可以在ES6的类中,混合使用原型模式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}

Rectangle.prototype.area = function area() {
return this.width * this.height;
};

const obj = new Rectangle(3, 5);

obj.area(); // 15