您的位置:首页 > Web前端 > JavaScript

深入理解javascript之继承

2015-10-27 08:56 483 查看
继承是面向对象编程的一个基础。javascript中的继承,主要是通过原型链来实现的。

原型链继承

实现原型链继承的基本模式代码如下:

/**
* @description: 原型继承基本模式
* @author:       刘放
* @date:         2015/10/26 15:52
*/
function Father(){
this.name = "刘放";
}
Father.prototype.getName = function(){
return this.name;
}
function Son(){
this.Sonname = "小放";
}
//继承Father
Son.prototype = new Father();
Son.prototype.getSonName = function(){
return this.Sonname;
};
var f = new Son();
document.write(f.getName());  //刘放


其原理就是子类的原型指向了父类的原型,从而新实例化的对象拥有了父类的属性和方法。原型链一层一层往上搜索直到Object.prototype为止。关于原型的概念,这里就不再细说了,可以参考该文章:深入理解原型

接下来我们说说如何确定原型和实体的关系。第一种是使用instanceof操作符。

//使用instanceof判断实例与原型的关系
document.write(f instanceof Object);//true
document.write(f instanceof Father);//true
document.write(f instanceof Son);//true


第二种是使用isPrototypeOf()方法,只要是原型链中出现过的原型,都可以说是该原型链所派生的实例的原型。

//使用isPrototypeOf()方法判断实例与原型的关系
document.write(Object.prototype.isPrototypeOf(f));//true
document.write(Father.prototype.isPrototypeOf(f));//true
document.write(Son.prototype.isPrototypeOf(f));//true


原型链虽热很强大,但是也存在一些问题。在使用原型继承时,原型实际上会变成另一个类型的实例。于是,原来的实例属性也就顺理成章地变成了现在的原型属性了。

//使用原型继承出现的一些问题,就是原型属性共享
function FatherB(){
this.age = [12,13,14];
}
function SonB(){

}
SonB.prototype = new FatherB();
var fB = new SonB();
fB.age.push(15);
document.write(fB.age);//12,13,14,15
//但是其他子类的属性也改变了
var fBB = new SonB();
document.write(fBB.age);//12,13,14,15


也就是说SonB的所有实例都会共享其属性。所以在实践中其实很少用到原型继承。

借用构造函数继承

为了解决原型链继承存在的共享属性问题。开发人员开始使用借用构造函数方式继承。实现就是在子类的内部调用父类的构造函数。在javascript中使用apply和call就可以改变作用域了,详细请看博文:call和apply

借用构造函数继承的基本模式如下:

/**
* @description: 借用构造函数继承基本模式
* @author:       刘放
* @date:         2015/10/26 16:27
*/
function FatherC(name){
this.name = name;
this.age = [12,13,14];
}
function SonC(){
FatherC.call(this,"刘放");
}
var fC = new SonC();
fC.age.push(15);
document.write(fC.age);//12,13,14,15
var fCC = new SonC();
document.write(fCC.age);//12,13,14
document.write(fCC.name);//刘放


可以看到,age属性不再共享,而且我们还可以向父类提供参数。

但是这种方法同样存在一个问题,就是方法都在构造函数中定义,那么函数的复用性就降低了。所以这种方法也很少单独使用。

组合继承

组合继承是将原型链继承和借用构造函数继承的优点结合,是最常用的继承。其思路是使用原型链对原型属性和方法继承,而通过构造函数实现对实例属性的继承。这样既能保证复用性,也能保证每个实例都拥有自己的属性。

组合继承的基本模式如下:

/**
* @description: 组合继承基本模式
* @author:       刘放
* @date:         2015/10/26 16:47
*/
function FatherD(name){
this.name = name;
this.age = [12,13,14];
}
FatherD.prototype.sayName = function(){
document.write(this.name);
}
function SonD(name,sex){
FatherD.call(this,name);
this.sex = sex;
}
//继承方法
SonD.prototype = new FatherD();
SonD.prototype.constructor = SonD;
SonD.prototype.saySex = function(){
document.write(this.sex);
}

var fD = new SonD("刘放","男");
fD.age.push(15);
document.write(fD.age);//12,13,14,15
fD.sayName();//刘放
fD.saySex();//男

var fDD = new SonD("小放","女");
document.write(fDD.age);//12,13,14
fDD.sayName();//小放
fDD.saySex();//女


组合继承虽然常用,但是不代表它没有问题,它最大的问题就是无论什么情况下,都会调用两次父类构造函数

FatherD.call(this,name);
SonD.prototype = new FatherD();


寄生式继承

寄生式继承的思路和工厂模式类型,即创建一个用于封装继承过程的函数,该函数在内部以某种方法来增强对象,最后再像真是它做了所有工作一样返回对象。对工厂模式不熟悉的同学可以参考博文:设计模式

寄生式继承的基本模式如下:

/**
* @description: 寄生式继承基本模式
* @author:       刘放
* @date:         2015/10/26 17:16
*/
function createSon(original){
var clone = Object.create(original);
clone.sayHi = function(){
document.write("nihao");
};
return clone;
}
var FatherE = {
name:"刘放",
age:[12,13,14],
};
var SonE = createSon(FatherE);
SonE.sayHi();//nihao
document.write(SonE.name);//刘放
//任何能够返回新对象的函数都适用于此模式,但是由于不能做到函数复用所以降低了效率


任何能够返回新对象的函数都适用于此模式,但是由于不能做到函数复用所以降低了效率。

寄生组合式继承

刚才也提到了,组合继承最大的问题就是调用了两次父类的构造函数,而我们利用寄生式和组合结合就可以解决该问题。

所谓寄生组合式继承,就是通过借用构造函数来继承属性,通过原型链的混成形式来继承方法。其基本思路是:不必为了指定子类型的原型而调用父类的构造函数,我们需要的其实就是父类原型的一个副本而已。本质上,就是使用寄生式继承来继承父类的原型,然后再将这些结果指定给子类的原型。

寄生组合式继承的基本模式如下:

/**
* @description: 寄生组合式继承基本模式
* @author:       刘放
* @date:         2015/10/26 17:30
*/
function Extends(son,father){
var prototype = Object.create(father.prototype);//创建对象
prototype.constructor = son;//增强对象
son.prototype = prototype;//指定对象
}
function FatherF(name){
this.name = name;
this.age = [12,13,14];
}
FatherF.prototype.sayName = function(){
document.write(this.name);
}
function SonF(name,sex){
this.name = name;
this.sex = sex;
}
//继承
Extends(SonF,FatherF);
SonF.prototype.saySex = function(){
document.write(this.sex);
}
var fF = new SonF("刘放","男");
fF.saySex();//男
fF.sayName();//刘放
//高效的只调用一次构造函数,最理想的继承方式。


寄生组合式继承解决了组合继承两次调用父类构造函数的弊端,也完成了继承所需的所有功能,是最理想的继承方式

最后,完整代码:demo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: