在JavaScript面向对象编程中使用继承(3)

简介:

上次讲了一下在使用JavaScript进行面向对象编程中,采用构造法来实现类继承的一些优缺点。下面我们接着把'原型继承法'的也有缺点也讲一讲,希望大家能积极提意见并探讨其中的一些问题。

    原型(prototype)是JavaScript实现面向对象编程的一个基础,但它并不是唯一的构造类的方法,我们完全可以不使用prototype而实现类的编写(把属性和方法的附加全都写在构造函数里面就行了)。不过原型除了可以为Object的子类添加新的属性和方法外,它还可以为脚本环境中的内部对象继续添加原型属性和方法,比如我们最常用的给内部对象String添加一个Trim方法来删除字符串两端的空格,代码为:

   String.prototype.Trim =  function()
   {
         return  this.replace(/(^\s*)|(\s*$)/g, ''); None.gif
   } None.gif

    这样我们就可以在任何的String实例中使用Trim方法了,用惯了这种原型系统,有的时候反而还觉得传统OOP没有它爽,kaka。言归正传,继续讲我们的原型继承法。

    原型继承法的原理:

    原型继承法的关键代码是其构造函数function ArrayList02()下的第一句:

   ArrayList02.prototype =  new CollectionBase();
   ArrayList02.prototype.constructor = ArrayList02;

    Ae 把prototype都覆盖成基类的一个实例了,子类还怎么使用prototype呢?这里不要着急,反正JavaScript的对象实例都是可以动态增删属性和方法的,基类实例作为prototype不就正好等于extends了CollectionBase吗?之后再使用XXX.prototype.xxx = function(),可以继续获得新增了属性和方法的对象。注意ArrayList02.prototype是在ArrayList02的构造函数外被初始化为基类的实例的

    再来看第二句,为什么要把ArrayList02赋值给新prototype的constructor呢?如果不做这个赋值,当我们从ArrayList02的实例中,通过???.prototype.constructor去却其构造函数,将会获得CollectionBase。这不是我们的本意,而且这是使用instanceof关键之比较对象实例和对象也会出错。

    原型继承法的缺陷:

    原型继承法有两个缺陷,第一个是由于类的原型(prototype)实际上是一个Object的实例,它不能再次被实例化(它的初始化在脚本装载时已经执行完毕)。这么意思呢?我们知道在新建对象实例时,我们使用语句new ArrayList02(),这时我们可以看做JavaScript脚本引擎把prototype的一个浅拷贝作为this返回给类的实例(这里其实没有发生拷贝,只是利用浅拷贝这个概念来帮助理解),如果类没有附加任何原型属性和原型方法,那么就等于返回了一个new Object()实例。问题就出在这里了,由于new对prototype执行的是浅拷贝,如果prototype的原型属性里有对象类型的属性,那么就会造成共享对象实例的问题(类似于在传统OOP的类定义中使用了static修饰符来修饰了属性)。这个缺陷下面会有示例演示,避免的办法就是不要在基类中定义对象类型的属性,比如: Array、Object和Date等。
    第二个缺陷和上次讲的"构造继承法"的缺陷差不多,也是关于子类定义是语句顺序的。就是说代码:ArrayList02.prototype = new CollectionBase();必须在所有prototype定义之前执行,很简单,如果在原型属性和方法都导入完成后,再执行这个语句,就定于把之前的导入全都覆盖掉了:(。解决办法就是按我给文章(1)中的那个顺序来写,郁闷吧?kaka

    原型继承法的示例:

ExpandedBlockStart.gif < script  language ="JavaScript" >
InBlock.gifdocument.write('原形继承法:
< br > '); 
InBlock.gif
var  arrayList21  =   new  ArrayList02();
InBlock.gifarrayList21.Add('a');
InBlock.gifarrayList21.Add('b');
InBlock.gifarrayList21.foo();
InBlock.gif
var  arrayList22  =   new  ArrayList02();
InBlock.gifarrayList22.Add('a');
InBlock.gifarrayList22.Add('b');
InBlock.gifarrayList22.Add('c');
ExpandedBlockEnd.gifarrayList22.foo();
None.gif
</ script >


    示例运行结果为:

None.gif原形继承法:
None.gif[class ArrayList02]: 2: a,b
None.gif[class ArrayList02]: 3: a,b,c,a,b


    发现问题了吧emteeth.gif?实例arrayList22的foo()居然输出了a,b,c,a,b@_@... 这就是前面说的prototype对象浅拷贝带来的问题。不过为什么arrayList22中的集合计数器却仍然是3呢?这是因为this.m_Count是整数类型,这种类型又叫值类型(和C#里的值类型、对象类型概念一样的)。值类型不存在浅拷贝和深拷贝的问题,可以看成都是深拷贝。

    小结:JavaScript的原型继承法,虽然有prototype浅拷贝那么严重的bug,不过它却是使用比较多的继承方式。因为我们很少在基类里定义属性,更别说对象类型的属性了,所以引发这个错误的可能性不是很大,只是它是个潜在的隐患:(。至于第二个缺陷,也是一个潜在bug,只要自己定义类的时候能比较清醒就不会犯错误。'重载'也比较简单,如果在ArrayList02.prototype = new CollectionBase();后有重名的属性或方法导入,自动覆盖基类中的属性或方法就像当于重载了。

    应用场景:基类没有属性,至少是要没有对象类型的属性。这种继承的优点是保持了子类构造函数的完整,可以不在里面添加任何和继承有关系的代码,所有继承和重载操作都由对原型(prototype)的操作来完成。


本文转自博客园鸟食轩的博客,原文链接:http://www.cnblogs.com/birdshome/,如需转载请自行联系原博主。

目录
相关文章
|
JavaScript 前端开发 Java
深入JS面向对象(原型-继承)(一)
深入JS面向对象(原型-继承)
30 0
|
1月前
|
JavaScript 前端开发
js开发:请解释原型继承和类继承的区别。
JavaScript中的原型继承和类继承用于共享对象属性和方法。原型继承利用原型链查找属性,节省内存但不支持私有成员。类继承通过ES6的class和extends实现,支持私有成员但占用更多内存。两者各有优势,适用于不同场景。
18 0
|
3月前
|
JavaScript
|
3月前
|
存储 前端开发 JavaScript
揭秘原型链:探索 JavaScript 面向对象编程的核心(下)
揭秘原型链:探索 JavaScript 面向对象编程的核心(下)
揭秘原型链:探索 JavaScript 面向对象编程的核心(下)
|
3月前
|
前端开发 JavaScript 开发者
揭秘原型链:探索 JavaScript 面向对象编程的核心(上)
揭秘原型链:探索 JavaScript 面向对象编程的核心(上)
揭秘原型链:探索 JavaScript 面向对象编程的核心(上)
|
3月前
|
存储 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(三)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程
|
3月前
|
设计模式 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(一)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程
|
3月前
|
存储 JavaScript 前端开发
构造函数和原型的结合应用:轻松搞定JS的面向对象编程(二)
构造函数和原型的结合应用:轻松搞定JS的面向对象编程
|
1月前
|
设计模式 JavaScript 前端开发
JavaScript中继承的优缺点
JavaScript中继承的优缺点
13 3
|
1月前
|
JavaScript 前端开发
如何在 JavaScript 中实现继承?
如何在 JavaScript 中实现继承?
11 2