javascript prototype 学习

http://www.cnblogs.com/birdshome/archive/2005/02/17/105403.html

原型(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)的操作来完成。

 

我们知道JScript中对象的prototype属性,是用来返回对象类型原型的引用的。我们使用prototype属性提供对象的类的一组基本功能。并且对象的新实例会"继承"赋予该对象原型的操作。

下面我们看三个经典的prototype属性的使用示例。

    1、为脚本环境内建对象添加方法:

 Array.prototype.max = function()
 {
     
var i, max = this[0];
     
for (i = 1; i < this.length; i++)
     {
       
if (max < this[i])
        max 
= this[i];
     }
    
return max;
 };

 2、为用户自定义类添加方法:

 function TestObject(name)
 {
     
this.m_Name = name;
 }

 TestObject.prototype.ShowName 
= function()
 {
     alert(
this.m_Name);
 };


3、更新自定义类的prototype:
 function TestObjectA()
 {
    
this.MethodA = function()
    {
       alert('TestObjectA.MethodA()');
    }
 }

 
function TestObjectB()
 {
    
this.MethodB = function()
    {
       alert('TestObjectB.MethodB()');
    }
 }

 TestObjectB.prototype 
= new TestObjectA();
 第三个很眼熟吧?对啊,它就是我们前面介绍的原型继承法呀~~ 不过今天我们不是研究"继承",之所以可以这样来实现一种继承,只是利用了prototype属性的一个副作用而已。
  prototype还有一个默认的属性:constructor,是用来表示创建对象的函数的(即我们OOP里说的构造函数)。constructor属性是所有具有prototype属性的对象的成员。它们包括除Global和Math对象以外的所有JScript内部对象。constructor属性保存了对构造特定对象实例的函数的引用。

posted @ 2009-11-12 20:40  code_flyer  阅读(386)  评论(0编辑  收藏  举报