在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函数。

 
 
 

javascript继承之借用构造函数与原型的更多相关文章

  1. javascript继承之借用构造函数(二)

    //简单的函数调用 function Father() { this.nums= [1,2]; } function Son() { Father.call(this);//调用超类型,完成son继承 ...

  2. javascript实现继承3种方式: 原型继承、借用构造函数继承、组合继承,模拟extends方法继承

    javascript中实现继承的三种方式:原型继承.借用构造函数继承.混合继承: /* js当中的继承 js中 构造函数 原型对象 实力对象的关系: 1 构造函数.prototype = 原型对象 2 ...

  3. js继承之组合继承(结合原型链继承 和 借用构造函数继承)

    在我的前两篇文章中,我们已经介绍了 js 中实现继承的两种模式:原型链继承和借用构造函数继承.这两种模式都存在各自的缺点,所以,我们考虑是否能将这二者结合到一起,从而发挥二者之长.即在继承过程中,既可 ...

  4. javascript中继承(二)-----借用构造函数继承的个人理解

    本人目录如下: 零.寒暄&回顾 一,借用构造函数 二.事件代理 三,call和apply的用法 四.总结 零.寒暄&回顾 上次博客跟大家分享了自己对原型链继承的理解,想看的同学欢迎猛击 ...

  5. js继承之借用构造函数继承

    我的上一篇文章介绍了,原型链继承模式.但是单纯的原型链模式并不能很好地实现继承. 一.原型链的缺点 1.1 单纯的原型链继承最大的一个缺点,来自于原型中包含引用类型的值. 本来,我们没有通过原型链实现 ...

  6. Javascript面向对象——创建对象、构造函数的原型

    Javascript面向对象--创建对象.构造函数的原型 其实:JavaScript中的创建类,就是创建一个构造函数,在创建对象时用到new这个关键字, 一.创建对象 1.函数创建对象 functio ...

  7. JS学习笔记——JavaScript继承的6种方法(原型链、借用构造函数、组合、原型式、寄生式、寄生组合式)

    JavaScript继承的6种方法 1,原型链继承 2,借用构造函数继承 3,组合继承(原型+借用构造) 4,原型式继承 5,寄生式继承 6,寄生组合式继承 1.原型链继承. <script t ...

  8. JS继承之借用构造函数继承和组合继承

    根据少一点套路,多一点真诚这个原则,继续学习. 借用构造函数继承 在解决原型中包含引用类型值所带来问题的过程中,开发人员开始使用一种叫做借用构造函数(constructor stealing)的技术( ...

  9. JavaScript之工厂方式 构造函数方式 原型方式讲解

    一.工厂方式可以为一个对象,创建多个实例. var oCar = new Object; oCar.color = "red"; oCar.doors=4; oCar.mpg=23 ...

随机推荐

  1. Java中间(三十五)-----Java详细设置(一个):请指定初始容量设置

    集合是我们在Java编程中使用很广泛的,它就像大海,海纳百川,像万能容器,盛装万物.并且这个大海,万能容器还能够无限变大(假设条件同意). 当这个海.容器的量变得很大的时候,它的初始容量就会显得很重要 ...

  2. 【日报C在23】堆和栈的深入了解

    每日一C之堆与栈的深入理解        每天拾一个C语言贝壳,厚积薄发,积跬步以致千里.  今日贝壳:内存中堆与栈的深入理解.认识一个清晰地内存                          假 ...

  3. 使用 CodeIgniter 框架快速开发 PHP 应用(六)

    原文:使用 CodeIgniter 框架快速开发 PHP 应用(六) 简化使用 Session 和安全理论说得够多了! 现在让我们开始写我们自己的应用. 在这一章里,我们将会大致描述一下我们要建立的一 ...

  4. 栈 <stack>

    STL: pop:完成的仅仅是移除最顶端的数据.如果访问最顶端的数据,需要使用top函数(这个操作通常也被称为peek). #include <stack> #include <cs ...

  5. 组态ORACLE 11G ADG

    一旦载10g的,没有票据.昨天使用duplicate方法一安装11g ADG,过程艰辛,记录: 一.环境配置 主图书馆 IP地址:192.168.233.128/24 操作系统版本号:rhel5.8 ...

  6. js敏感词过滤

    var filterWord={ words:"", tblRoot:{}, //敏感词文件 file:"sensitiveWords.txt", //载入敏感 ...

  7. Call to undefined function mssql_connect()错误解决

    原文:Call to undefined function mssql_connect()错误解决 同事用php+mssql修改一个系统,却一直配置不了环境.遂做了一个测试,一般情况下我们会注意php ...

  8. 几款web版整合(QQ、msn、icq、yahoo通、Gtalk等)即时聊天http://hi.baidu.com/ejie/blog/item/e89794ef9a9431efce1b3ebd.html

        直到近期为止,我们经常使用的即时聊天工具(QQ.msn等)了Web版,大家不用下载庞大软件,直接打开网页就能够与自己的好友聊天,很方便.在此将时汇总,便于大家查找,节约大家一点时间. 此都是官 ...

  9. MapReduce的InputFormat学习过程

    昨天,经过几个小时的学习.该MapReduce学习的某一位的方法的第一阶段.即当大多数文件的开头的Data至key-value制图.那是,InputFormat的过程.虽说过程不是非常难,可是也存在非 ...

  10. Write the code.Change the world.---WWDC2014