继承是面向对象编程中讨论最多的话题。很多面向对象语言都支持两种继承:接口继承和实现继承。前者只继承方法签名,后者继承实际的方法。接口继承在 ECMAScript 中是不可能的,因为函数没有签名。实现继承是 ECMAScript 唯一支持的继承方式,而这主要是通过原型链实现的。 在这里顺便补充一下函数签名以及解释一下为什么在ECMAScript中没有函数签名。
函数签名(或者类型签名或方法签名):定义了函数或方法的输入与输出;签名可包含:
1)参数及参数的类型返回值及其类型;
2)可能抛出或传出的异常
3)该方法在面向对象程序中可用性方面的信息(如public、static或prototype)
为什么在ECMAScript中没有函数签名?
我们都知道,JavaScript是一钟松散型或动态语言。这意味着你不必提前声明变量的类型。类型将在程序处理时自动确定。JavaScripl中的签名仍然可以为你提供有关该方法的一些信息:可以理解为js是属于弱语言,没有强的数据类型(强语言的签名是函数的参数必须定义,而js函数中的参数(形参)可以不用)。
那么我们现在从几个方面来了解一下原型链继承吧
目录
1.原型链
ECMAScript把原型链定义为ECMAScript的主要继承方式,基本思想是通过原型继承多个引用了类型的属性和方法。每个构造函数都有一个原型对象,原型有一个属性指回构造函数,而实例有一个内部指针指向原型,这是构造函数、原型和实例的关系,我们可以通过如下图解来理解
如果原型是另一个类型的实例呢?那就意味着这个原型本身有一个内部指针指向另一个原型,相应地另一个原型也有一个指针指向另一个构造函数。这样就在实例和原型之间构造了一条原型链。这就是原型链的基本构想,如图所示:
代码如下:
// 创建Animal
function Animal() {
this.name = 'animal';
}
Animal.prototype.getAnimalName = function () {
console.log(this.name + 'getAnimalName');
}
// 创建Dog
function Dog() {
this.name = 'dog';
}
//将Animal的实例赋值给Dog.prototype
Dog.prototype = new Animal();
//在Dog.prototype中添加方法
Dog.prototype.getDogName = function () {
console.log(this.name + 'getDogName');
}
var d1 = new Dog();
d1.getAnimalName()
d1.getDogName()
注意: 在使用原型链继承的时候,要在继承之后再去原型对象上定义自己所需的属性和方法
运行结果如下:
以上代码定义了两个类型:Animal 和 Dog。
这两个类型分别定义了一个属性和一个方法。这两个类型的主要区别是通过创建 Animal 的实例并将其赋值给Dog的原型对象,所以Dog. prototype 实现了对 Animal 的继承。这个赋值重写了 Dog 最初的原型,将其替换为Animal 的实例。这意味着 Animal 实例可以访问的所有属性和方法也会存在于 Dog. prototype。这样实现继承之后,代码紧接着又给Dog.prototype,也就是这个 Animal 的实例添加了一个新方法。最后又创建了 Dog 的实例并调用了它继承的 getAnimalName方法。
下图展示了子类的实例与两个构造函数及其对应的原型之间的关系。