JavaScript Prototype 详解

本文里讲述的是关于JavaScript的prototype问题,至于具体的JavaScript面向对象的编程教程,请各位看客到其他网站搜索一下,或者到这里看看。

首先开始一个例子,如下:

1functionA(){

2this.t1="ffffff";

3this.t2=function(msg){

4alert(msg);

5};

6};

7

8A.prototype.p1="xxxx";

9

10A.prototype.f1=function(){

11dosomething.

12};

其实p1,f1是对function的prototype对象的操作,大家要明白,function

也是一个对象,对象也有属性,而prototype就是function的属性,该属性

也是一个对象,不同之处是,function在做为类定义的时候,创建类实例的

过程(new的过程)要参照它的prototype对象,把prototype对象的所有

属性(也就是Java里的成员,包括成员变量和成员函数)都复制到新的对象

中去,所以可以看出prototype就是模板,而这个模板是在new一个对象之

前就已经存在了。

上面的JavaScript就好像在定义一个Java类,书写类的时候,除了用不同的声明

(Class和Function)区别,基本没有其他的区别,但在运行时有很大的区别。

首先Java要求类必须被编译成字节码才能被载入虚拟机,而JavaScript是在运行

代码的同时,执行了类似Java的编译载入的过程。并且Java的类在载入虚拟机

后一般就不能再改变类的定义了,比如把一个方法的行为改变或指向另一个方

法的引用等。而JavaScript在运行期还可以通过prototype来改变类及所有该类生成

的对象的行为。例如上面的例子中,在解析完functionA的函数体后,整个类也

就生成了,这时候如果new的话就能得到类的实例,紧接着的代码又向类动态

添加了新的行为。

而在functionA的函数体内定义的this成员,可以理解为‘后’绑定成员。

可以这么理解,在newA()的时候JavaScript建立了一个临时对象,

把A.prototype的所有成员复制到临时对象中,然后再把函数A中

定义的this成员也绑定到临时对象中,然后把临时对象返回给用户。

下面是模拟JavaScript的new关键字的处理伪过程:

//建立临时对象

vartobj={};

//复制prototype

for(varkeyinA.prototype)

tobj[key]=A.prototype[key];

//绑定函数体内的this成员(这个过程是JavaScript的内部处理,没有办法模拟)

returntobjtouser;

之所以存在function内部定义的this成员,以及prototype的成员是

有原因的。由于JavaScript的类在构造时是可以传递构造参数的,

所以,this成员的行为可能由于参数的不同而不同。这也就是需要后

绑定的原因了。在看下一个例子:

1functionAA(val1,val2){

2this.test1=function(){

3alert(val1);

4};

5

6this.test2=val2?function(){returnthis.test1;}:function(){return456;};

7

8this.test3=val1?val1:function(){alert("noval1");};

9}

这个例子很好的说明了后绑定的实际使用价值,所以后绑定对于成员

函数来说是非常有用的,对于成员变量来说其实没什么实际用处。

唯一不同的是,this成员在每次new对象时都要被JavaScript引擎解析,

原因很简单,根据不同的构造参数,使它们在运行期的行为可能有很大

的不同。而prototype的成员就不会每次都解析,第一次定义prototype

成员时才解析,以后可以直接引用prototype成员,并且更改了prototype

成员,所有已经建立的实例对象的相应成员都会被更改。

在运行期可以通过'对象名.成员名'来更改成员,这种方式可以更改this成员

和prototype成员的默认定义,但是更改只限于自身对象,因为JavaScript

和Java一样,也是传值,对象的引用也是一个地址值,所以new一个对象后,

prototype的成员也被复制到那个对象上了,再更改那个对象的成员,只会

影响那个对象自身,其他从同一个类new出来的对象都不会有任何变化。

不能通过运行期设置'类.prototype.成员名'来覆盖this同名成员,这样做没有

任何效果。

通过复制一个对象的所有属性到一个新对象,是不能通过修改prototype成员

来修改新对象的成员行为,因为新对象不是通过原来对象的类new出来的。

通常的复制方法如下:

1vartobj={};

2for(varkeyinotherObj)

3tobj[key]=otherObj[key];

看似tobj和otherObj的行为是一致的,他们不是一个类new出来的。

一个很好的办法可以测试,比如otherObj是A类new出来的,

可以通过使用(tobjinstanceofA)来测试,结果显然是false。

最新的测试表明,这种复制方法可以复制所有自定义方法,

但是系统提供的默认方法是不能被复制的,即使你显式的覆盖了

系统默认提供的方法,如toString方法等。

最后再谈谈prototype的constructor成员,该成员是对一个类的构造

函数的引用,在类定义的初期,如果一个类没有从其他别的类那

里继承,该类的prototype.constructor属性保存的是该类自身的引用,

如果该类从别的类继承,那么它的constructor属性就保存了父类的

constructor引用,一般constructor没有什么用处,但可以通过它来取

得他的类的信息,就像Java里的对象都有getClass()方法,constructor

就是干这个用的。有它的好处是,再运行期可以改变所有同类对象

的成员行为,如:

1someObj.constructor.prototype.somePrototype=function(){

2otherprocess.

3}

因此好的习惯是在继承之后把prototype的constructor成员设置一下,

否则会把父类的prototype成员改掉,那样程序的行为就不可预知了。

如:

1functionclassA(){

2

3}

4

5classB.prototype=newclassA();

6classB.prototype.constructor=classB;

7

我要说的关于JavaScript的prototype属性就这么多,大家多提意见多交流。

相关推荐