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函数。
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。