总结继承的几种方式

简单总结继承的几种方式

JavaScript作为一门弱类型的语言,本着精简的原则,它取消了类的概念,只有对象的概念,

更是有万物皆对象的说法。在基于类的面向对象方式中,对象(object)依靠类(class)来产生。

而在基于原型的面向对象方式中,对象(object)则是依靠构造器(constructor) 利用 原型(prototype)

构造出来的。而JavaScript语言正是如此,它是通过一种叫做原型(prototype)的方式来实现面向对象编程的。

它和其他的面向对象类编程语言一样,只是它的实现方式不同而已,或者说他们采用了不同的面向对象设计哲学。

那么下面就让我们来简单总结一下继承的几种方式:

  1. 扩展原型对象实现继承

构造函数有一个 prototype 属性,指向的就是原型对象,通过给原型对象添加属性和方法,让构造函数的实例

都可以访问到,从而实现继承

function Animal(name,color,say){
  this.name = name;
  this.color = color;
  this.say = function(){
    console.log('喵喵喵');
  }
}

var cat = new Animal('cat','white');
// 如果给Animal的prototype属性上添加个 cry 方法 ,那么实例对象 cat将也会有 cry方法 
Animal.prototype.cry = function(){
  console.log('呜呜呜');
}
  1. 替换原型对象实现继承 (常用类型)
  • 为什么会有该方式呢?
    其实上面扩展原型对象的方法已经很方便了,但是为什么我们还需要使用该方式呢?试想一下,
    当我们需要给构造函数的原型对象添加许多属性和方法的时候,岂不是要写很多冗余(重复)的代码。例如上面的例子
    看下面代码:

        // 我们要给 构造函数Animal 添加很多的方法 即:
        Animal.prototype.aaa = function(){};
        Animal.prototype.bbb = function(){};
        Animal.prototype.ccc = function(){};
        Animal.prototype.ddd = function(){};
        Animal.prototype.eee = function(){};
        ......  // 诸如这样的话,代码就显得不那么优雅,高效了吧。
    
  • 实现方式?
    重新给构造函数的prototype属性(原型对象)赋值,指向一个全新的对象,在这个对象中添加属性和方法,注:
    一定要添加一个constructor属性,并且指向构造函数本身 具体看代码:

        Animal.prototype = {
          // 一定要指明constructor属性,不然的话,会根据原型链查找一直到Object.prototype
          constructor : Animal;  
          saying : function(){},
          crying : function(){},
          doing : function(){}
         ......
        }
    
  1. 混入继承
  • 混入继承的使用场景:已知对象 o1, o2, 需要把 o1中的功能(属性方法)拷贝到 o2 中去

  • 实现方式 :用 for...in... 对 o 进行遍历(可以将混入继承的模式封装成函数),如下所示

      // target : 目标对象(接收数据的对象)
      // source : 接收对象 (数据从哪个对象中来)
      function mixin(target,source){
          for(var key in source){
            target[key] = source[key];
          }
          return target;
      }
    
  • 原理其实很简单,jQuery中的$.extend 方法利用的就是混入继承的原理

  1. 原型 + 混入继承
  • 本质上就是对混入继承的一次运用

  • 只不过目标对象为原型对象而已

          // 运用上面封装的混入继承的函数  将对象{a:10,b:20,c:function(){}}的功能考本到Animal原型对象上去
          mixin(Animal.prototype, {a:10,b:20,c:function(){}} );  
          // 还可以 这样操作 给Animal构造函数 的原型对象添加一个extend方法,在该方法中调用mixin函数,这样的话也可以实现
          Animal.prototype.extend = function(){
               mixin(Animal.prototype, source);
          }
          // 不过两种方法没有本质之差,看大家易于接受哪个了
    
  1. 经典继承 —> 道格拉斯《JavaScript语言精粹》中提到的一种继承模型
  • 实现的功能:已知一个对象 o1 需要创建一个新的对象 o2 ,这个新的对象需要继承自对象 o1,代码如下:

        // 可以将经典继承 封装成一个函数
        function create(o){
          function F(){};    // 创建一个构造函数
          F.prototype = o;   // 将F 的原型指向 对象 o;
          return new F();    // 将 构造函数的实例 返回出去,这样的话 F的实例对象,就会继承自 o
        }
        var o2 = create(o1);  // 即:o2 继承于 o1 ;  o2.__proto__ = o1;
    
  • 使用场景 :要创建一个对象(不需要关心构造函数),新对象需要继承自另一个指定的对象

  • ES5中 :Object.create( ) 的实现原理就源自于经典继承

  1. 借用构造函数 实现继承
  • 先看一下代码 再解释:

      // 需要两个构造函数
      function Person(name,age,gender){  
       this.name = name;
       this.age = age;
       this.gender = gender;
      }
      // function Student(name,age,){
      //  this.name = name;
      // this.age = age;
      //  this.gender = gender;
      // }
      // 这样的话 name,age 属性都一样,就会产生重复的代码 我们可以巧妙的利用call 来简单的实现
      function Student(name,age){
       Person.call(this,name,age);     // 其实就是用 call的方法,call借用Person的功能
       this.gender = gender;
      }
    
    • 借用Person中的构造函数的功能,this表示构造函数的实例,即Student的实例对象可以继承name,age属性;

    • 借用构造函数实现继承,子构造函数借用父构造函数来完成,给子类Student的实例添加属性

    • 注意:由于要借用父类构造函数,所以父类构造函数的功能对子类对象要适用,例如下面情况就最好不用call

      // 需要两个构造函数
      function Person(name,age,gender){  
       this.name = name;
       this.age = age;
       this.gender = gender;
      }
      // function Student(name,age,){
      //  this.name = name;
      // this.age = age;
      // }
      function Student(name,age){
       Person.call(this,name,age);     
      }
      
  • 上述情况,就最好不要使用call来借用父类构造函数Person的功能了,因为,gender属性是Student子类构造函数
    并不需要的,这样的话,就会在Student中产生不必要的属性和方法,如果子类函数还要有gender方法的话,那么就会和之前的产生冲突,交叉污染

  • 其他情况:遇到需要实现功能,该对象上没有这个功能,可以适当地去寻找已经有功能的对象

原文地址:https://www.cnblogs.com/guoqi77/p/Q_Inherit.html