继承
已知一些矩阵是对称的。 如果沿左上角到右下角的对角线翻转对称矩阵,它保持不变。 换句话说,存储在x,y
的值总是与y,x
相同。
想象一下,我们需要一个像Matrix
这样的数据结构,但是它必需保证一个事实,矩阵是对称的。 我们可以从头开始编写它,但这需要重复一些代码,与我们已经写过的代码很相似。
JavaScript 的原型系统可以创建一个新类,就像旧类一样,但是它的一些属性有了新的定义。 新类派生自旧类的原型,但为set
方法增加了一个新的定义。
在面向对象的编程术语中,这称为继承(inheritance)。 新类继承旧类的属性和行为。
class SymmetricMatrix extends Matrix {
constructor(size, element = (x, y) => undefined) {
super(size, size, (x, y) => {
if (x < y) return element(y, x);
else return element(x, y);
});
}
set(x, y, value) {
super.set(x, y, value);
if (x != y) {
super.set(y, x, value);
}
}
}
let matrix = new SymmetricMatrix(5, (x, y) => `${x},${y}`);
console.log(matrix.get(2, 3));
// → 3,2
extends
这个词用于表示,这个类不应该直接基于默认的Object
原型,而应该基于其他类。 这被称为超类(superclass)。 派生类是子类(subclass)。
为了初始化SymmetricMatrix
实例,构造器通过super
关键字调用其超类的构造器。 这是必要的,因为如果这个新对象的行为(大致)像Matrix
,它需要矩阵具有的实例属性。 为了确保矩阵是对称的,构造器包装了content
方法,来交换对角线以下的值的坐标。
set
方法再次使用super
,但这次不是调用构造器,而是从超类的一组方法中调用特定的方法。 我们正在重新定义set
,但是想要使用原来的行为。 因为this.set
引用新的set
方法,所以调用这个方法是行不通的。 在类方法内部,super
提供了一种方法,来调用超类中定义的方法。
继承允许我们用相对较少的工作,从现有数据类型构建稍微不同的数据类型。 它是面向对象传统的基础部分,与封装和多态一样。 尽管后两者现在普遍被认为是伟大的想法,但继承更具争议性。
尽管封装和多态可用于将代码彼此分离,从而减少整个程序的耦合,但继承从根本上将类连接在一起,从而产生更多的耦合。 继承一个类时,比起单纯使用它,你通常必须更加了解它如何工作。 继承可能是一个有用的工具,并且我现在在自己的程序中使用它,但它不应该成为你的第一个工具,你可能不应该积极寻找机会来构建类层次结构(类的家族树)。