JavaScript中的继承

JavaScript中的继承是通过原型链实现的,有几种设计继承的方法总结如下:

一. 原型链

  基本思想:每个构造函数都有一个原型对象,原型对象又都包含一个指向构造函数的指针,而实例又指向一个原型对象的指针,那么,如果我们让原型对象等于另一个类型的实例,此时的原型对象将包含一个指向另一个原型的指针,相应的另一个原型中也包含着一个指向另一个构造函数的指针,假如另一个原型又是另一个类型的实例呢,那么上述的关系依然成立,如此层层递进,就构成了实例和原型的链条。下面是例子

 1 function SuperType(){
 2     this.property = true;
 3 }
 4 SuperType.prototype.getSuperValue = function(){
 5     return this.property;
 6 }
 7 function SubType(){
 8     this.subproperty = false;
 9 }
10 //继承SuperType
11 SubType.prototype = new SuperType();
12 SubType.prototype.getSubValue = function(){
13     return this.subproperty;
14 };
15 var instance = new SubType();
16 alert(instace.getSuperValue());//true

缺点:引用类型值的属性会被所有实例共享,假设有两个子类型的实例,其中一个实例修改了从超类型继承而来的引用类型的值,修改的结果也同样会在另一个实例中显示出来;

        创建子类型的实例的时候,不能向超类型的构造函数中传递参数。

二.借用构造函数

     基本思想:在子类型构造函数的内部调用超类型构造函数,即通过apply和call方法来调用构造函数

 1 function SuperType(){
 2    this.colors = ["red","blue","green"];
 3 }
 4 function SubType(){
 5    //继承了SuperType
 6    SuperType.call(this);
 7 }
 8 var instance1 = new SubType();
 9 instance1.colors.push("black");
10 alert(instance1.colors);//"red,blue,green,black"
11 var instance2 = new SubType();
12 alert(instance2.colors);//"red,blue,green"

优点:解决了原型中包含引用类型值所带来的问题

缺点:超类型的原型中定义的方法,对子类型而言也是不可见的(没有指向超类型原型的指针,只有指向构造函数的),结果所有类型都只能使用构造函数模式,这样肯定是不好的。

三.组合继承

基本思想:使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承下面是例子

 1 function SuperType(name){
 2   this.name = name;
 3   this.colors = ["red","blue","green"];
 4 }
 5 SuperType.prototype.sayName = function(){
 6   alert(this.name);
 7 };
 8 function SubType(name,age){
 9   //继承属性
10   SuperType.call(this,name);
11   this.age = age;
12 }
13 //继承方法
14 SubType.prototype = new SuperType();
15 SubType.prototype.constructor = SubType;
16 SubType.prototype.sayAge = function(){
17   alert(this.age);
18 };
19 var instance1 = new SubType("Nicholas",29);
20 instance1.colors.push("black");
21 alert(instance1.colors);//"red,blue,green,black"
22 instance1.sayName();//"Nicholas"
23 instance1.sayAge();//29
24 var instance2 = new SubType("Greg",27);
25 alert(instance2.colors);//"red,blue,green"
26 instance2.sayName();//"Greg"
27 instance2.sayAge();//27

组合继承是最常用的继承模式,而且,利用instanceof 和isprototypeof()也能够用于识别基于组合继承创建的对象

四.原型式继承

基本思想:借助原型可以基于已有的对象创建新对象,同时,不必因此创建自定义类型。下面是例子

 1 function object(o){
 2   function F(){};
 3   F.prototype = o;
 4   return new F();
 5 }
 6 var person = {
 7   name:"Nicholas",
 8   friends:["shelby","Cour","Van"]
 9 };
10 var anotherPerson = object(person);
11 anotherPerson.name = "Greg";
12 anotherPerson.friends.push("Rob");
13 var yetAnotherPerson = object(person);
14 yetAnotherPerson.name = "Linda";
15 yetAnotherPerson.friends.push("Barbie");
16 alert(person.friends);//"Shelby,Court,Van,Rob,Barbie"

缺点:还是包含引用类型值得属性始终会共享相应的值,实例中修改会在原型中展现。

        无论什么情况下都会调用两次超类型构造函数:一次是在创建子类型原型的时候,另一次是在子类型构造函数内部,子类型最后会包含超类型中的全部实例属性,但有时候我们会在调用子类型构造函数时重写这些属性。

五.寄生式继承

基本思路:与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式类增强对象。下面是例子:

 function object(o){
    function F(){};
    F.prototype = o;
    return new F();
 }
function createAnother(original){
  var clone = object(original);//通过调用函数来创建一个新对象
  clone.sayHi = function(){
     alert("hi");
  };
  return clone;
}
var person = {
  name:"Nicholas",
  friends:["Shelby","Court","Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi();//"Hi"

使用场景:在主要考虑对象而不是自定义类型和构造函数的情况下。

六.寄生组合式继承

     为了解决组合继承的缺点

     基本思想:通过借用构造函数来继承属性,通过原型链的混成形式来继承方法

function object(o){
    function F(){};
    F.prototype = o;
    return new F();
 }
function inheritPrototype(subType,superType){
  var prototype = object(superType.ptorotype);//创建对象
  prototype.constructor = subType;//增强对象
  subType.prototype = prototype;//指定对象
}

  能够正常使用instanceof() 和 isPrototypeOf()。

posted @ 2017-05-03 18:52  孙冬冬-入门前端  阅读(116)  评论(0编辑  收藏  举报