先来说说第一种继承方式,原型链继承。
一. 原型链继承
所谓原型链继承,就是让父类的一个实例作为子类的原型。
即 :
parentInstance = new Parent(); child.prototype = parentInstance;
这样,在创建子类的实例时,子类实例的__proto__指向父类的实例(即此时子类构造函数的prototype属性),而父类实例的__proto__又指向父类构造函数的prototype属性。借用这种方式形成了一条原型链。
由于JavaScript中搜索实例中调用的变量有如下方式:
在当前实例中寻找变量名
在当前实例所指向的原型中寻找
假设原型链中有如下继承关系:
grandparent(有方法 grandparent.prototype.sayHello) -> parent -> child
当在child的实例child_ming调用方法 sayHello 时,首先在child_ming中(即只定义在child_ming这一个实例中,而非所有实例中)搜索sayHello,并未找到,然后开始搜索它所指向的原型,即parent的实例。在parent的实例中也没有此方法,开始搜索parent的原型,即grandparent的实例。在grandparent的实例中依然没有找到,又搜索grandparent的原型并找到该方法。
可以看出,这样便实现了继承。
如同在使用prototype创建对象时遇到的问题,倘若完全使用原型链进行继承,会使得一些需要继承但不需要在不同实例间进行共享的属性变得不方便实现。
下面就要说一说借用构造函数实现的继承。
二. 借用构造函数实现继承
所谓借用构造函数实现继承,即在子类的构造函数中把父类的构造函数借来使用,以求在子类中生成父类的属性。
看如下代码:
function Parent(color){ this.color = color; this.getColor = function(){ alert(this.color); }; } function Child(color,size){ Parent.call(this,color); this.size = size; }
这就是一个简单的借用构造函数的继承。
通过使用父类的构造函数,可以使得同一构造函数的不同的实例的同一属性拥有不同的值。解决了原型链继承中的属性共享的弊端。
然而,如同使用构造函数创建对象时遇到的问题,通过构造函数生成的方法面临着重复定义的问题,同一类下的不同实例拥有各自的方法,而一般来讲方法是需要实现复用的,没有必要让它们拥有各自的方法。
使用组合继承可以解决这个问题。
三. 组合继承(原型链与借用构造函数)
既然原型链可以实现属性共享,借用构造函数可以实现属性值的私有,不妨将它们结合起来,这就形成了组合继承。
所谓组合继承,实际上就是用一次原型链继承,用一次借用构造函数继承。
看如下代码:
function Parent(color){ this.color = color; } Parent.prototype.getColor = function(){ alert(this.color); }; function Child(color,size){ Parent.call(this,color); //借用构造函数继承属性 this.size = size; } //下面的属性'green'并没有影响,这里要使用原型继承方法 Child.prototype = new Parent('green'); var child_demo = new Child('red',200);
首先思考这样两个问题,创建一个child 实例到底生成了几份color 属性?在代码中既定义了'red' 又定义了 ‘green',当调用 child_demo.getColor() 时,到底会alert 哪一个?
首先来看第一个问题。由于子类构造函数中借用了父类的构造函数,在创建子类实例时必然会生成一次color 属性。但是不要忘记,我们在继承方法时是让子类构造函数的原型指向一个父类的实例,在创建这个父类实例时还要生成一次color 属性(即上面'green'处),而这个属性是完全没有必要存在的。所以一共生成了两份color 属性,一个有用一个没用。
再来看第二个问题。只要能理解this 的含义就可以知道:
child_demo.getColor() // 'red' Child.prototype.getColor() //'green'
组合继承结合了前两种继承方式的优点,但它也有自己的缺点。从生成两份color 属性可以知道在继承过程中调用了两次Parent 的构造函数,这会造成执行完成速度的问题,影响了效率。但是瑕不掩瑜,这种继承方式还是成为了JavaScript中最常用的继承模式。
四. 原型式继承
原型式继承是从已有对象的基础上继承,基于已有对象创建新的对象。
看如下代码:
var obj = { color: 'red', getColor: function(){ alert(this.color); }, }; //getChild(obj)返回的是一个__proto__指向obj的实例 function getChild(obj){ function func(){} func.prototype = obj; return new func(); } var child_demo = getChild(obj);
这种继承方式与原型链式的继承方式有相同点和不同点。
相同点:它们都是通过改变子类构造函数的原型属性来实现继承,所继承的属性都具有不同实例共享的特点。