在开始摆弄代码之前,应该搞清楚使用继承的目的和能带来什么好处。一般来说,在设计类的时候,我们希望能减少重复性的代码,并且尽量弱化类之间的耦合。而要做到这两者都兼顾是很难的,我们需要根据具体的条件和环境下决定我们应该采取什么方法。根据我们对面向对象语言中继承的了解,继承会带类直接的强耦合,但js由于其特有的灵活性,可以设计出强耦合和弱耦合,高效率和低效率的代码。而具体用什么,看情况。
下面提供js实现继承的三种方法:类式继承,原型继承,掺元类。这里先简述类式继承,后两种在往后的随便中简述,请多多关注、指导,谢谢。
类式继承。
js类式继承的实现依靠原型链来实现的。什么是原型链?js中对象有个属性prototy,这个属性返回对象类型的引用,用于提供对象的类的一组基本功能。
貌似对prototype有印象,对了,我们经常这样用代码。
1
2
3
4
5
6
7
8
9
|
var Person = function (){
this .name = "liyatang" ;
}; Person.prototype = { //可以在这里提供Person的基本功能
getName : function (){
return this .name;
}
} |
我们把类的基本功能放在prototype属性里,表示Person这个对象的引用有XXX功能。
在理解原型后,需要理解下什么是原型链。在访问对象的某个成员(属性或方法)时,如果这个成员未见于当前对象,那么js会在prototype属性所指的那个对象中查找它,如果还没有找到,就继续到下一级的prototype所指的对象中查找,直至找到。如果没有找到就会返回undifined。
那么原型链给我们什么提示呢?很容易联想到,原型链意味着让一个类继承另一个类,只需将子类的prototype设置为指向父类的一个实例即可。这就把父类的成员绑定到子类上了,因为在子类上查找不到某个成员时会往父类中查找。(以上这两段用词不严谨,只在用通俗易懂的言语描述)
下面我们需要个Chinese类,需要继承Person类的name和getName成员。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
var Chinese = function (name, nation){
//继承,需要调用父类的构造函数,可以用call调用,this指向Chinese
//使Person在此作用域上,才可以调用Person的成员
Person.call( this ,name);
this .nation = nation;
}; Chinese.prototype = Person.prototype; //这里不可和以前一样,因为覆盖掉了prototype属性 //Chinese.prototype = { // getNation : function(){ // return this.nation; // } //}; //以后的方法都需要这样加 Chinese.prototype.getNation = function (){
return this .nation;
}; |
继承关系就建立了,我们这样调用它
1
2
|
var c = new Chinese( "liyatang" , "China" );
alert(c.getName()); // liyatang
|
于是类式继承就这样完成了。难道真的完成了嘛,用firebug在alert那里设断点,会发现原来的Person.prototype被修改了,添加了getNation方法。
这是因为在上面的代码Chinese.prototype = Person.prototype; 这是引用类型,修改Chinese同时也修改了Person。这本身就是不能容忍的,且使类之间形成强耦合性,这不是我们要的效果。
我们可以另起一个对象或实例化一个实例来弱化耦合性。
1
2
3
4
5
6
|
//第一种 //Chinese.prototype = new Person(); //第二种 //var F = function(){}; //F.prototype = Person.prototype; //Chinese.prototype = F.prototype; |
这两种方法有什么区别呢。在第二种中添加了一个空函数F,这样做可以避免创建父类的一个实例,因为有可能父类会比较庞大,而且父类的构造函数会有一些副作用,或者说会执行大量的计算任务。所以力荐第二种方法。
到此,完了嘛,还没有!在对象的属性prototype下面有个属性constructor,它保存了对构造特定对象实例的函数的引用。根据这个说法Chiese.prototype.constructor应该等于Chinese,实际上不是。
回忆之前在设置Chiese的原型链时,我们把Person.prototype 覆盖掉了Chiese.prototype。所以此时的Chiese.prototype.constructor是Person。我们还需要添加以下代码
1
2
3
4
|
//对这里的if条件不需要细究,知道Chinese.prototype.constructor = Chinese就行 if (Chinese.prototype.constructor == Object.prototype.constructor){
Chinese.prototype.constructor = Chinese;
} |
整理全部代码如下
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
|
var Person = function (name){
this .name = name;
}; Person.prototype = { getName : function (){
return this .name;
}
}; var Chinese = function (name, nation){
Person.call( this ,name);
this .nation = nation;
}; var F = function (){};
F.prototype = Person.prototype; Chinese.prototype = F.prototype; if (Chinese.prototype.constructor == Object.prototype.constructor){
Chinese.prototype.constructor = Chinese;
} Chinese.prototype.getNation = function (){
return this .nation;
}; var c = new Chinese( "liyatang" , "China" );
alert(c.getName()); |
如果可以把继承的代码放在一个函数里,方便代码复用,最后整理代码如下
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
|
function extend(subClass,superClass){
var F = function (){};
F.prototype = superClass.prototype;
subClass.prototype = new F();
subClass.prototype.constructor = subClass;
subClass.superclass = superClass.prototype; //加多了个属性指向父类本身以便调用父类函数
if (superClass.prototype.constructor == Object.prototype.constructor){
superClass.prototype.constructor = superClass;
}
} var Person = function (name){
this .name = name;
}; Person.prototype = { getName : function (){
return this .name;
}
}; var Chinese = function (name, nation){
Person.call( this ,name);
this .nation = nation;
}; extend(Chinese, Person); Chinese.prototype.getNation = function (){
return this .nation;
}; var c = new Chinese( "liyatang" , "China" );
alert(c.getName()); |
发表后修改:
在一楼的评论下,我对那个extend函数又有新的看法。之前在讨论如何设置原型链时提出了两种方法
1
2
3
4
5
6
|
//第一种 //Chinese.prototype = new Person(); //第二种 //var F = function(){}; //F.prototype = Person.prototype; //Chinese.prototype = F.prototype; |
虽然第二种减少了调用父类的构造函数这条路,但在设计Chinese类时用了Person.call(this,name);这里也相当于调用了父类的构造函数。
然而用第一种方法的话可以减少在Chinese中再写Person.call(this,name);,这部分代码在子类中往往会遗忘。不妨把这种功能代码放在了extend里。就只写
Chinese.prototype = new Person();也达到同样的目的:耦合不强。
但遗忘的一点是,Chinese.prototype = new Person();这样写对嘛。答案是不对!很明显 new Person()需要传一个name参数的。我们不可能在extend函数里做这部分工作,只好在Chinese类里调用父类的构造函数了。这样也符合面向对象的思路。
所以,还是力荐用第二种方法。
第一次这样写有关技术类的文章,基本是按自己的思路铺展开来,难免会有一些没有考虑到的地方和解释的不清楚的地方,望留言反馈,谢谢。
相关推荐
js中实现多态采用和继承类似的方法.docx
本文实例讲述了javascript基于prototype实现类似OOP继承的方法。分享给大家供大家参考,具体如下: 这里要说明的是,公有属性(使用this.修饰符)可以被覆盖,私有属性(使用var 修饰符)不能被覆盖 子类不能访问父类...
多态的实现可以采用和继承类似的方法。首先定义一个抽象类,其中调用一些虚方法,虚方法在抽象类中没用定义,而是通过其具体的实现类来实现。 如下面的例子: Object.extend=function(destination,source){ for...
(ES6有关键字class和extend,继承的语法与Java等面向对象语言类似,但是,ES6 class,只是JavaScript原型继承的语法糖而已) 1. 类式继承 关键点:通过构造函数实现继承。 父类: function Parent(name) { this....
这种说法原因一般都是觉得javascript作为一门弱类型语言与类似java或c#之类的强型语言的继承方式有很大的区别,因而默认它就是非主流的面向对象方式,甚至竟有很多书将其描述为’非完全面向对象’语言。其实个人觉得...
一、原型链继承 在原型链继承方面,JavaScript与java、c#等语言类似,仅允许单父类继承。prototype继承的基本方式如下: 代码如下: function Parent(){} function Child(){} Child.prototype = new Parent(); 通过...
支持播放暂停,时间轴起始点拖动,自动生成鹰眼轨迹,车辆运动速度控制
主要是要写的代码太多了,也许是js有特殊的处理数组的方式,真是我不知道而已,但是我真的想自己给js实现一个类似ruby的迭代器的东东,而且实现起来也不难,那就开始动手吧. 真的应该庆幸js是动态语言啊,如果是静态语言,...
Underscore 是一个JavaScript实用库,提供了类似Prototype.js的一些功能,但是没有继承任何JavaScript内置对象。它弥补了部分jQuery没有实现的功能,同时又是Backbone.js必不可少的部分。 Underscore提供了80多个函数,...
这种说法原因一般都是觉得javascript作为一门弱类型语言与类似java或c#之类的强型语言的继承方式有很大的区别,因而默认它就是非主流的面向对象方式,甚至竟有很多书将其描述为’非完全面向对象’语言。其实个人觉得...
JS相关 文章目录写在前面的话:1. ES5和ES6继承方式区别2.... 手动实现map(filter以及forEach也类似)15. js实现checkbox全选以及反选16. 对原型链的理解?prototype上都有哪些属性17. 为什么使用继承1
本文将介绍两种很类似于对象冒充的继承方式,即使用call()和apply()方法
118行实现的一个简单的Javascript的对象模型 这个文件中定义了类模型中类似于其他面向对象语言中的Object类。 如何新建类 作为基类,类的定义使用 var NewClass = Class.new(); 来进行定义。 通过以上方式定义的类...
14.10 用JavaScript实现数组排序 14.11 数字千分位函数 14.12 读写Cookie的函数 14.13 获取JavaScript函数中的所有参数 14.14 奇偶数的判断 14.15 在JavaScript运行VBScript函数 14.16 购物篮中常用的计算总价效果 ...
js函数的原型对象constructor默认指向函数本身,原型对象除了有原型属性外,为了实现继承,还有一个原型链指针__proto__,该指针指向上一层的原型对象,而上一层的原型对象的结构依然类似,这样利用__proto__一直...
站点索引页面声明“除非另有说明,本站点上的所有代码和文档均属于公共领域”纯功能的面向对象系统目前的代码实现了一个无类的、基于委托的 OO 系统,类似于 Self 或 Javascript 的系统。 这是一个成熟的 OO 系统,...
精炼JS强大而灵活的生成器对象,具有继承的可能性这个迷你库添加/扩展了 JavaScript 中对象的可能性,或者更准确地说,为您处理对象的所有日常工作。 即: 跨浏览器兼容性您无需担心在不同浏览器中实现的问题,库会...
第1章 页面特效 ...1.2 页面自动最大化 1.3 页面自动刷新 1.4 页面的后退、刷新、前进 1.5保护网页源代码 ...22.15 用prototype实现JavaScript的继承 22.16 JavaScript制作哈希表 第23章 其他技巧及特效 23.1 ...