• 总结继承的几种方式


    简单总结继承的几种方式

    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方法的话,那么就会和之前的产生冲突,交叉污染

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

  • 相关阅读:
    关于idea中启动clean时Process terminated报错
    关于idea启动jsp时候Please, configure Web Facet first!
    关于Javaweb中jstl的foreach不能显示数据的问题
    关于Javaweb中报错信息Cause: java.sql.SQLException: Unknown initial character set index '255' received from server.解决办法
    关于使用idea 进行druid的数据库连接报错解决Cannot resolve com.mysq.jdbc.Connection.ping method. Will use 'SELECT 1' instead
    关于c3p0中显示数据库连接超时处理方法
    havel定理
    Skier
    扩展欧几里德算法(待补充)
    next_permutation(全排列)
  • 原文地址:https://www.cnblogs.com/guoqi77/p/Q_Inherit.html
Copyright © 2020-2023  润新知