zoukankan      html  css  js  c++  java
  • javascript继承之借用构造函数与原型

        在js中,关于继承只有利用构造函数和原型链两种来现实。以前所见到的种种方法与模式,只不过是变种罢了。

    借用构造函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    // 一个动物类,包含名字和性别属性
    function Animal (name, sex) {
        this.name = name;
        this.sex = sex;   
        this.getName = function(){ 
           return this.name;   
        };      
    }
     
    // Cat类继承Animal基类,并且拥有额外的属性
    function Cat (name, sex, hasLegs) {
        this.hasLegs = hasLegs;
        Animal.apply(this, arguments);// 借用Animal的构造器
    }
     
    // Dog类继承Animal基类,并且拥有与Cat类不一样的额外的属性
    function Dog (name, sex, otherFeatures) {
        this.otherFeatures= otherFeatures;
        Animal.apply(this, arguments); // 借用Animal的构造器
     
    }

         借用构造函数的优点就是能够复用代码;缺点就是它不能继承基类的原型,以及部分代码累赘。像Animal类中的getName方法,本该有一个就可以了,但是每次调用其构造器都会开辟新的空间来存放这个方法。如果把这些共有的属性或者方法放入原型链中,就不会需要每个实例都有一个这样的属性或者方法,而是大家共用一个模板。

    构造函数与原型并用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 重新定义动物类,
    function Animal (name, sex) {
        this.name = name;
        this.sex = sex;   
    }
     
    // 提取公共的方法或者属性放入原型链中
    Animal.prototype.getName = function (){ return this.name;}
     
    //Cat类不变,修改Cat的原型链,使其指向基类的原型
    Cat.prototype = Animal.prototype;
     
    //Dog类不变,修改Dog的原型链,使其指向基类的原型
    Dog.prototype = Animal.prototype;

     测试代码1

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    // 分别new一个对象
    var cat = new Cat('咪咪', 'female', true),
     
         dog = new Dog('汪汪', 'male', null);
     
    // 功能已实现
    console.log(cat.getName(),dog.getName()); // 咪咪 汪汪
     
    // 新的问题1
    console.log(cat instanceof Cat, dog instanceof Cat); // true true 现在猫狗不分了
     
    /*原因是在改变各个具体子类的原型是,它们的构造器都指向了基类,它们拥有同一个构造器。
    如果修改某个子类的原型constructor,必然会影响到其它子类*/
     
    // 新问题2。如果现在Cat类的getName逻辑有变,不能修改基类的原型。现作出如下改动
    function Cat (name, sex, hasLegs) {
        this.hasLegs = hasLegs;
        Animal.apply(this, arguments);
         // 新的逻辑
        this.getName = function (){
            return this.name+','+this.sex;
        }  
    }
     
    //但是这样代码又不能达到复用,因为每个Cat实例都有一个getName方法。
    /*如何解决上述问题呢,也许你想到了——原型【链】。突出个链字,链说明是一节一节的,如果
    我们在子类与基类原型中间再加一节,不就完事了么*/
     
    //定义一个空函数来做这个节点
    function o (){}
     
    // 让‘空’节点指向基类的原型,Cat类再指向空节点
    o.prototype = Animal.prototype;
    Cat.prototype = new o();
    // 重置Cat的构造器指针
    Cat.prototype.constructor = Cat;
     
    o.prototype = Animal.prototype;
    Dog.prototype = new o();
    Dog.prototype.constructor = Dog;

     完整的代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    // 一个动物类,包含名字和性别属性
    function Animal (name, sex) {
      this.name = name;
      this.sex = sex;
     
    }
    // 提取公共的方法或者属性放入原型链中
    Animal.prototype.getName = function (){ return this.name;}
     
    function o (){}
    var oCat = new o();   
    // 修改Cat类的getName逻辑
    oCat.getName = function (){return this.name+','+this.sex;}
     
    o.prototype = Animal.prototype;
    Cat.prototype = oCat;
    //重值Cat构造器指针
    Cat.prototype.constructor = Cat;
    // 同上。并且这三行代码的顺序不能随意改动
    o.prototype = Animal.prototype;
    Dog.prototype = new o();
    Dog.prototype.constructor = Dog;
     
     
    // Cat类继承Animal基类,并且拥有额外的属性
    function Cat (name, sex, hasLegs) {
      this.hasLegs = hasLegs;
      Animal.apply(this, arguments);
    }
     
    // Dog类继承Animal基类,并且拥有与Cat类不一样的额外的属性
    function Dog (name, sex, otherFeatures) {
      this.otherFeatures= otherFeatures;
      Animal.apply(this, arguments);
     
    }
     
    var cat = new Cat('咪咪', 'female', true),
     
    dog = new Dog('汪汪', 'male', null);
     
    // 功能正常,代码也达到进一步复用
    console.log(cat.getName(), dog.getName());
    // 现在猫是猫,狗是狗了
    console.log(cat instanceof Cat, dog instanceof Cat);
    // 两个子类的构造器也是对的了
    console.log(cat.constructor, dog.constructor);

     现在似乎完整了,可是好像还是有些遗憾。如同被妹子拒了一样:你人很好,我们还是做朋友吧。言外之意就是还没好到让妹子想跟你在一起的程度。那么哪里不够呢?现在只有两个子类,如果有几十个的话,还是要做很多重复的工作;如果又有一个机械的基类,又要做同样的事情。那么,我们可以把这个继承的方法写成面向对象的形式么?答案是:可以滴。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    // 将继承的实现细节用函数包裹起来,classPropers是需要覆盖的属性和方法
     
    function inherit(classPropers){
         var o = function (){}, // 空函数用做空节点
              parent = this, // 这里的this代表基类构造器
              child = function(){}, // 返回一个子类
              hasOwnConstructor = false; // 是否拥有自己的构造器
         if(typeof classPropers === 'object'
            && classPropers.hasOwnProperty('constructor')){
             //如果有构造器属性,则覆盖构造器
             child = function (){
                  classPropers.constructor.apply(this,arguments);   
             }
             hasOwnConstructor = true;
              
         }else{
             // 否则使用基类的构造器
              child = function(){
                   parent.apply(this, arguments);
              }
         }
         o.prototype = parent.prototype;
         child.prototype = new o();
         if(hasOwnConstructor){
            // 重置构造器指针
            child.prototype.constructor = classPropers.constructor
         }
         if(classPropers){
             /*$.extend是jQ函数,这里不再实现。如果classPropers与基类有相同的方法,则会‘覆盖’
             基类的方法*/
             $.extend(child.prototype, classPropers);     
         }
         // 继承基类的静态方法,这样子类还可以被继承
         $.extend(child, parent);
         child.__super__ = parent.prototype; // 以防万一以后还要调用基类相同方法
     
         return child;
     
    }        

     完整测试代码2

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    // 一个动物类,包含名字和性别属性
        function Animal (name, sex) {
            this.name = name;
            this.sex = sex;
       
        }
        Animal.prototype = {
            getName: function(){ return this.name;},
            getSex: function(){ return this.sex;}
        };
        function inherit(classPropers){
         var o = function (){}, // 空函数用做空节点
              parent = this, // 这里的this代表基类构造器
              child = function(){}, // 返回一个子类
              hasOwnConstructor = false; // 是否拥有自己的构造器
         if(typeof classPropers === 'object'
            && classPropers.hasOwnProperty('constructor')){
             //如果有构造器属性,则覆盖构造器
             child = function (){
                  classPropers.constructor.apply(this,arguments);  
             }
             hasOwnConstructor = true;
               
         }else{
             // 否则使用基类的构造器
              child = function(){
                   parent.apply(this, arguments);
              }
         }
         o.prototype = parent.prototype;
         child.prototype = new o();
         if(hasOwnConstructor){
            // 重置构造器指针
            child.prototype.constructor = classPropers.constructor
         }
         if(classPropers){
             /*$.extend是jQ函数,这里不再实现。如果classPropers与基类有相同的方法,则会‘覆盖’
             基类的方法*/
             $.extend(child.prototype, classPropers);    
         }
         // 继承基类的静态方法,这样子类还可以被继承
         $.extend(child, parent);
         child.__super__ = parent.prototype; // 以防万一以后还要调用基类相同方法
      
         return child;
      
        
        Animal.inherit = inherit;
     
        var Cat = Animal.inherit({sayHi:function(){console.log('喵喵...')}}),
        cat = new Cat('咪咪', '不告诉你');
     
        console.log(cat.getName(),cat.getSex());
     
      var Dog = Animal.inherit({
                    constructor:function(name){
                      this.name = name;
                      console.log('我有自己的工厂(构造器)');
                    }
                }),
      dog = new Dog('我为自己代言');
      console.log(dog.getName(),dog.constructor);
     
      // 老虎小时候就是猫,不信,我有证据如下。
      var Tiger = Cat.inherit({constructor:function(){console.log('出来一声吼啊!喵喵......咋变猫叫了呢?wuwu...')}}),
      tiger = new Tiger();
     
      tiger.sayHi();

     记得引用jQuery或者自己实现$.extend函数。

     
     
     
  • 相关阅读:
    mac OSX 上 brew install hive
    mac osx 系统 brew install hadoop 安装指南
    python在windows平台的pip安装package时的编译问题
    安装scapy遇到的问题
    node中使用domain处理异步异常问题
    web开发中的 emmet 效率提升工具
    未来10年房地产存量与人口需求量分析
    Apple individual program 加入之后的玩法 官方资源
    nginx 反向代理 与 Apache backend的配置联合配置
    ubuntu 休眠之后网络间接失败 can not connect to network after suspend (wake up)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3548557.html
Copyright © 2011-2022 走看看