你真的了解prototype吗?



JavaScript中的prototype,即构造函数的原型对象,我想对于使用过JavaScript oo模式编程的你来说,肯定都能很好的利用prototype的特性来编写出优美的面向对象的代码,因为它的确很好用。但是你真的足够了解prototype吗?

首先我觉得,在分析prototype之前,应该先了解为什么要使用prototype来实现继承,而并不是把所有属性写到构造函数内部,那么这样做优势体现在哪里呢,如果你跟我有一样的困惑,请看如下两段代码:

01 /*代码1*/
02 var F = function(){
03     this.a = 1;
04     this.b = 2;
05     this.c = 3;
06     this.d = 4;
07     this.e = 5;
08     this.f = 6;
09 };
10 for(var i=0;i<100000;i++){
11     eval('var f' + i + '= new F;');
12 }
13 /*代码2*/
14 var F = function(){};
15 F.prototype = {
16     a: 1,
17     b: 2,
18     c: 3,
19     d: 4,
20     e: 5,
21     f: 6
22 };
23 for(var i=0;i<100000;i++){
24     eval('var f' + i + '= new F;');
25 }

在浏览器中分别执行两段代码,观察浏览器的内存占用情况(数据量比较大,等待脚本执行完毕,浏览器稳定后的数据),如下图:

从图片中的数据不难看出,在Firefox中,把所有属性全部写到构造的原型对象中,生成100000个实例对象时,占用内存会比把所有属性全部写 到构造内部少15M,而在IE中,这个差距竟然达到97M,匪夷所思啊,神奇的IE。这也就是为什么写oo代码时,总是喜欢把类的一些属性和方法写到它的 原型对象中,在达到同样目的的前提下节省内存的使用率,我想没有哪一个人愿意拒绝这样的做法。

缘何使用prototype会节省内存空间呢?我觉得有必要了解JavaScript中类的实例化过程。

01 var F = function(){
02     this.a = 1;
03 };
04 F.prototype = {
05     a: 2   
06 };
07 var f = new F;
08      
09 /*new F的过程*/
10 var f = {};    //创建空对象
11 f.__proto__ = F.prototype;  //设置对原型对象的引用
12 F.call(f);     //拷贝构造中的属性,初始化对象

通过这个实例化过程,可以看到,在每个实例化对象中,如果构造中属性全部放到原型对象中来实现继承(所谓的原型链继承),并不是拷贝原型中的全部属 性到自身所占的空间内,而是通过__proto__来实现对F.prototype的引用,显而易见,这样做更能节省内存空间。

事实上,这个过程可以解释我们再开发中遇到的很多问题,比如,在构造内部定义的属性要比在原型中定义的属性优先级要高,正如上述代码片段,f.a =1而不是f.a=2,就是因为对原型对象的引用发生在对象初始化之前,详细见下图:

另外一个问题,修改原型对象中的属性或方法,即使在实例化之后,依然会奏效,这里需要注意的是,这个修改的过程需在F.prototype指向的原始对象上修改,不要试图将prototype重新指向一个新的对象,因为在实例化之后,在实例对象f中查找某个属性时,依然会从F.prototype指向的原始对象中查找,也就是说并不没有改变f.__proto__的指向,虽然我们期望看到他重新指向一个新的对象。

PS:说了很多,重点只有一个,就是深刻类的实例化过程,这才是JavaScript原型继承的本质。

posted @ 2011-02-28 13:32  snowjake  阅读(336)  评论(2编辑  收藏  举报