在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/,如需转载请自行联系原博主。

目录
相关文章
|
4天前
|
JavaScript 前端开发
Javascript如何实现继承?
【10月更文挑战第24天】JavaScript 中实现继承的方式有很多种,每种方式都有其优缺点和适用场景。在实际开发中,我们需要根据具体的需求和情况选择合适的继承方式,以实现代码的复用和扩展。
|
2月前
|
自然语言处理 JavaScript 前端开发
一文梳理JavaScript中常见的七大继承方案
该文章系统地概述了JavaScript中七种常见的继承模式,包括原型链继承、构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合继承等,并探讨了每种模式的实现方式及其优缺点。
一文梳理JavaScript中常见的七大继承方案
|
24天前
|
JavaScript 前端开发 Java
JavaScript中的面向对象编程(OOP) - 终极指南
本文介绍了 JavaScript 的面向对象编程 (OOP) 概念,包括继承、多态、封装和抽象等关键要素,并通过代码示例帮助开发者理解和应用 OOP 思维。
32 5
|
2月前
|
JavaScript 前端开发
js之class继承|27
js之class继承|27
|
2月前
|
JavaScript 前端开发 Java
js面向对象编程|24
js面向对象编程|24
|
2月前
|
JSON JavaScript 前端开发
js原型继承|26
js原型继承|26
|
2月前
|
JavaScript 前端开发 开发者
JavaScript 类继承
JavaScript 类继承
18 1
|
2月前
|
JavaScript 前端开发
JS的几种继承方式
JavaScript中的几种继承方式视频。
13 0
|
3月前
|
开发者 图形学 iOS开发
掌握Unity的跨平台部署与发布秘籍,让你的游戏作品在多个平台上大放异彩——从基础设置到高级优化,深入解析一站式游戏开发解决方案的每一个细节,带你领略高效发布流程的魅力所在
【8月更文挑战第31天】跨平台游戏开发是当今游戏产业的热点,尤其在移动设备普及的背景下更为重要。作为领先的游戏开发引擎,Unity以其卓越的跨平台支持能力脱颖而出,能够将游戏轻松部署至iOS、Android、PC、Mac、Web及游戏主机等多个平台。本文通过杂文形式探讨Unity在各平台的部署与发布策略,并提供具体实例,涵盖项目设置、性能优化、打包流程及发布前准备等关键环节,助力开发者充分利用Unity的强大功能,实现多平台游戏开发。
91 0
|
3月前
|
JavaScript 前端开发 开发者
揭开JavaScript的神秘面纱:原型链背后隐藏的继承秘密
【8月更文挑战第23天】原型链是JavaScript面向对象编程的核心特性,它使对象能继承另一个对象的属性和方法。每个对象内部都有一个[[Prototype]]属性指向其原型对象,形成链式结构。访问对象属性时,若当前对象不存在该属性,则沿原型链向上查找。
31 0