javascript继承

1.原型链法
原型链是JavaScript中继承的主要方法。
每个构造函数都拥有一个原型对象,原型对象都包含一个指向构造函数的指针(constructor),实例都包含一个指向原型对象的内部指针(proto)。如果将一个类的实例赋值给另一个类的原型会这样?像下面的代码:

//定义父类
 function Person(name,age,job){
        this.name=name;
        this.age=age;
        this.job=job;

 }
        Person1.prototype={
            constructor:Person,
            sayName:function(){
            console.log(this.name);
        }
    }
//定义子类
    function Chinese(){}
//将它的原型替换为Person类的实例
    Chinese.prototype=new Person('xiaohua',20,'gongren');
//定义子类实例,并调用sayName方法
    var b=new Chinese();
    b.sayName();//xiaohua

由此可知,子类已经可以使用父类的属性和方法。

原理:重写子类原型对象,用父类的实例赋值给Chinese.prototype实现继承,这时Chinese类的原型被修改了,新原型对象是Person的实例,那么Chinese.prototype就有了跟这个Person的实例一样的属性和方法(这里是name,age,job三个属性),那么子类实例当然可以使用子类原型中的这些属性和方法,这个新原型会包含指向另一个原型的指针(proto),这样构成了原型链。
子类实例在调用sayName方法时,会经历3个先后步骤:(1)搜索实例本身 (2)搜索Chinese类的原型 (3)沿着原型链搜索Person类的原型。最后在这里找到了这个方法。下面是类,原型和实例的关系图
这里写图片描述
原型链方法最主要的问题还是来自有引用类型的原型,我们以前说过包含引用类型值的原型属性会被所有实例共享,这也是为什么要在构造函数中定义属性,而不是在原型中定义属性的原因了。这里如果在Person的构造函数里,增加一句this.friends=[“Shelby”,”Court”,”Van”];,那么Chinese的原型中就会引用类型变量friends,所有Chinese实例共享引用类型的问题就出现。
2.借用构造函数
在解决原型中包含引用类型值所带来的问题的过程中,我们可以使用一种叫借用构造函数的技术(又称伪造对象或经典继承)。就是在子类型构造函数的内部调用父类型构造函数,记住,函数只不过是在特定环境中执行代码的对象,可以通过使用apply()和call()方法也可以在(将来)新创建的对象上执行构造函数。


 function Person(){

        this.friends=["Shelby","Court","Van"];
 }

function Chinese(){ 
    //继承了Person
    Person.call(this); 
} 

var a =new Chinese(); 
a.friends.push("Jack");    
alert(a.friends);           //"Shelby","Court","Van","Jack" 

var b=new Chinese(); 
alert(b.friends);          //"Shelby","Court","Van"

借用构造函数的问题
如果仅仅使用借用构造函数,那么将无法避免构造函数模式存在的问题——方法都在构造函数中定义,函数的复用也无从谈起。所以借用构造函数也很少单独使用。
3.组合继承
组合继承(伪经典继承),指的是将原型链和借用构造函数的技术组合在一块。其思路是使用原型链对共享属性和方法的继承,而通过借用构造函数来实现对实例对象的继承。这样,既通过在原型上定义的方法实现了函数复用,又能保证每个实例都有它自己的属性。

function Person(name){ 
    this.name=name; 
    this.friends=["Shelby","Court","Van"];
    } 

Person.prototype.sayName=function(){ 
    alert(this.name); 
    } 

function Chinese(name,age){ 
    //继承属性 
    Person.call(this,name); 
    this.age=age; 
} 

Chinese.prototype=new Person(); 
Chinese.prototype.constructor=Chinese; 
Chinese.prototype.sayAge=function(){ 
    alert(this.age); 
    } 


var a=new Chinese("Henry",29); 
a.friends.push("Jack");    
alert(a.friends);              //"Shelby","Court","Van","Jack" 
a.sayName();                  //Henry
a.sayAge();                   //29      

var b=new Chinese("Greg",28); 
alert(b.friends);              //"Shelby","Court","Van"
b.sayName();                 //Greg     
b.sayAge();                  //28

组合继承避免了原型链和借用构造函数的缺陷,融合了他们的有点,成为JavaScript中追尾常用的继承模式。而且,instanceof和isPrototypeof()也能够用于识别组合继承创建的对象。
4.原型式继承
​借助原型可以基于已有的对象创建新对象,同时还不必创建父类构造函数和实例化。

function object(o){ 
    function F(){}; 
    F.prototype=o; 
    return new F(); 
}

在object()函数内部,先创建一个临时性的构造函数,然后将传入的对象作为该构造函数的原型,最后返回这个临时类型的一个新实例。本质上讲,object()对传入其中的对象进行了一次浅复制,也就是用一个对象来覆盖一个临时性的构造函数的原型,这样这个构造函数的实例就继承那个传递对象的属性和方法。
但包含引用类型值的属性始终会共享相应的值,这和原型模式一样的。如下:

function object(o){  
    function F(){};  
    F.prototype=o;  
    return new F();  
} 
var person={ 
    name:"Henry", 
    friends:["Shelby","Court","Van"] 
}; 

var a=object(person); 
a.name="Greg"; 
a.friends.push("Rob"); 

var b=object(person); 
b.name="Linda"; 
b.friends.push("Brabie"); 

alert(person.friends); //"Shelby,Court,Van,Rob,Brabie"

5.寄生式继承
寄生式继承的思路与寄生构造函数和工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式类增强对象,最后再像真的是它做了所有工作一眼放回对象。

function creteAnother(original){ 
    var clone=object(original);   //通过调用函数创建一个新对象 
    clone.sayHi=function(){       //以某个方式来增强这个对象 
        alert("hi"); 
    } 
    return clone;                 //返回这个对象           
} 

var person={ 
    name:"zxj", 
    friends:["Shelby","Court","Van"] 
}; 

var anotherPerson=createAnother(person); 
anotherPerson.sayHi();  //"hi"

使用寄生式来为对象添加函数,会由于不能做到函数服用而降低效率。这一点与构造函数模式类似

6.寄生组合式继承
组合继承是JavaScript中最常用的继承模式,但它无论在什么情况下,都会调用两次父类型构造函数:一次在创建子类型原型时,一种在子类型构造函数内部。

function SuperType(name){ 
    this.name=name; 
    this.colors=["red","blue","green"]; 
    } 
function SubType(name,age){ 
    //继承属性 
    SuperType.call(this,name); //第二次调用父类型 
    this.age=age; 
} 

SubType.prototype=new SuperType();  //第一次调用父类型 
SubType.prototype.constructor=SubType; 
SubType.prototype.sayAge=function(){ 
    alert(this.age); 
    }

这样导致的结果是,实例中和子类原型中都包含了相同的属性name和colors,只是运行时实例中的属性屏蔽了子类原型中的属性,这样的话原型中的这些属性就没有存在的必要了。
而寄生组合式继承就能解决这个问题,所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链来继承方法。思路:不必为了指定子类型的原型而调用父类型的构造函数,我们只需父类型原型的一个副本。本质上,就是使用寄生式继承来继承父类型的原型,然后将结果指定给子类型的原型,寄生组合式继承的基本模式如下:

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

示例中inheritPrototype()函数实现了寄生组合式继承的最简单形式。这个函数接收两个参数:子类型构造函数和父类型构造函数。在函数内部:第一步是创建父类型原型的一个副本。第二步是为创建的副本调价constructor属性,从而弥补因重写原型而失去默认的constrcutor属性。最后一步,将新创建的对象(即副本)赋值给子类型的原型。这样,我就可以调用inheritPrototype()函数的语句,去替换前面例子中为子类型原型赋值的语句。

function object(o){ 
          function F(){} 
          F.prototype = o; 
          return new F(); 
      } 

      function inheritPrototype(subType, superType){ 
          var prototype = object(superType.prototype);   //create object 
          prototype.constructor = subType;               //augment object 
          subType.prototype = prototype;                 //assign object 
      } 

      function SuperType(name){ 
          this.name = name; 
          this.colors = ["red", "blue", "green"]; 
      } 

      SuperType.prototype.sayName = function(){ 
          alert(this.name); 
      }; 

      function SubType(name, age){   
          SuperType.call(this, name); 

          this.age = age; 
      } 

      inheritPrototype(SubType, SuperType); 

      SubType.prototype.sayAge = function(){ 
          alert(this.age); 
      }; 

      var instance1 = new SubType("Nicholas", 29); 
      instance1.colors.push("black"); 
      alert(instance1.colors);  //"red,blue,green,black" 
      instance1.sayName();      //"Nicholas"; 
      instance1.sayAge();       //29 


      var instance2 = new SubType("Greg", 27); 
      alert(instance2.colors);  //"red,blue,green" 
      instance2.sayName();      //"Greg"; 
      instance2.sayAge();       //27
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值