深入理解javascript之继承

来源:互联网 发布:易我数据恢复下载 编辑:程序博客网 时间:2024/04/26 13:09

继承是面向对象编程的一个基础。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

2 0
原创粉丝点击