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

简介:

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

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

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

    这样我们就可以在任何的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

    原型继承法的示例:

< script  language ="JavaScript" >
document.write('原形继承法:
< br > '); 
var  arrayList21  =   new  ArrayList02();
arrayList21.Add('a');
arrayList21.Add('b');
arrayList21.foo();
var  arrayList22  =   new  ArrayList02();
arrayList22.Add('a');
arrayList22.Add('b');
arrayList22.Add('c');
arrayList22.foo();
</ script >


    示例运行结果为:

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


    发现问题了吧?实例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/,如需转载请自行联系原博主。

目录
相关文章
|
1月前
|
JavaScript 前端开发
如何在 JavaScript 中使用 __proto__ 实现对象的继承?
使用`__proto__`实现对象继承时需要注意原型链的完整性和属性方法的正确继承,避免出现意外的行为和错误。同时,在现代JavaScript中,也可以使用`class`和`extends`关键字来实现更简洁和直观的继承语法,但理解基于`__proto__`的继承方式对于深入理解JavaScript的面向对象编程和原型链机制仍然具有重要意义。
|
1月前
|
JavaScript 前端开发
Javascript如何实现继承?
【10月更文挑战第24天】JavaScript 中实现继承的方式有很多种,每种方式都有其优缺点和适用场景。在实际开发中,我们需要根据具体的需求和情况选择合适的继承方式,以实现代码的复用和扩展。
|
1月前
|
JavaScript 前端开发
如何使用原型链继承实现 JavaScript 继承?
【10月更文挑战第22天】使用原型链继承可以实现JavaScript中的继承关系,但需要注意其共享性、查找效率以及参数传递等问题,根据具体的应用场景合理地选择和使用继承方式,以满足代码的复用性和可维护性要求。
|
1月前
|
JavaScript 前端开发 开发者
js实现继承怎么实现
【10月更文挑战第26天】每种方式都有其优缺点和适用场景,开发者可以根据具体的需求和项目情况选择合适的继承方式来实现代码的复用和扩展。
31 1
|
3月前
|
自然语言处理 JavaScript 前端开发
一文梳理JavaScript中常见的七大继承方案
该文章系统地概述了JavaScript中七种常见的继承模式,包括原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承等,并探讨了每种模式的实现方式及其优缺点。
一文梳理JavaScript中常见的七大继承方案
|
2月前
|
JavaScript 前端开发 Java
JavaScript中的面向对象编程(OOP) - 终极指南
本文介绍了 JavaScript 的面向对象编程 (OOP) 概念,包括继承、多态、封装和抽象等关键要素,并通过代码示例帮助开发者理解和应用 OOP 思维。
41 5
|
3月前
|
JavaScript 前端开发
js之class继承|27
js之class继承|27
|
3月前
|
JavaScript 前端开发 Java
js面向对象编程|24
js面向对象编程|24
|
3月前
|
JSON JavaScript 前端开发
js原型继承|26
js原型继承|26
|
3月前
|
JavaScript 前端开发 开发者
JavaScript 类继承
JavaScript 类继承
22 1