JavaScript 原型继承开端

      1.原型继承本质

      就javascript对象系统的实现来讲,对象并没有原型,而构造器有原型(构造器.prototype指向其原型)。对象只有构造自某个原型的说法,并没有持有某个原型的说法。原型其实也是一个对象实例。原型的含义是指:如果构造器有一个原型对象A,则由该构造器创建的实例都必然复制自A。

 

      2.空的对象是所有对象的基础

来看看以下代码:

JavaScript 原型继承开端
 1 <html>
 2         <head>
 3         <meta  http-equiv="content-type" charset="utf-8"/>
 4         <script type="text/javascript">
 5         var proto = Object.prototype;
 6         
 7         var count = 0;
 8         for(var n in proto)
 9         {
10            count++;    
11         }
12         alert(count);//return 0;
13         </script>
14         </head>
15         <body> 
16         </body>
17 </html>
JavaScript 原型继承开端

由上边代码我们知道Object()构造器的原型就是一个空的对象。下边的两行代码无非就是从Object.prototype复制出的一个映像,也是空的对象

obj1 = new Object();

obj2 = {};

构建过程可以简单理解为”复制”

JavaScript 原型继承开端

 

                                       图 2.1 对象构建

 

      3.构造复制---写复制---读遍历

由图 2.1可以看出,每构造出一个实例,都需要从原型中复制出一个相应的实例出来,但这种构造复制会产生一个问题:内存空间的急剧消耗。

因此我们的另一个策略就是写时复制,先看下图:

JavaScript 原型继承开端

                                   图 3.1 写时复制

在系统中只要将obj1和obj2的引用指向对象的原型,这样在读取的时候顺着引用读取即可。只有当需要修改对象obj1或obj2的属性时才复制一个映像出来,以后就操作该映像。写时复制的优点是只在第一次写的时候用一些代码来分配内存,带来一些代码和内存上的开销,以后就没有这种开销了。但如果需要经常写操作,则这种方式与上一种没有什么区别。

      第三种策略就是把写复制的粒度再次变小,从原型变成成员,仅当写某个实例的成员时,将成员信息复制到实例映像中。

JavaScript 原型继承开端

                          图 3.2 成员复制

我们发现obj2仍旧指向原型,在写操作时也没有与原型相同大小的实例被创建出来,不会导致大量的内存分配,因此在内存使用上更加经济。但obj2需要维护一张成员列表,列表中存放修改了的成员,列表中的成员是否与原型中的成员是否一致并不重要,只需要遵循两条规则。

1)保证读取时先被访问到。我们访问obj2.value时,得到10.但对于obj1.value,这时读遍历规则就起作用了:

2)如果对象中没有指定属性,则会尝试遍历对象的整个原型链,知道找到該值或者为空。

假设原型也持有一张成员列表,如下:

JavaScript 原型继承开端

                                图 3.3 读遍历

那么,obj2.value为10,而obj1.value自然为’abc’。我们也发现原型带来的另外一个效果:obj1.value与obj2.value的类型并不相同。如果原型链上没有维护value这个成员,那么obj1.vlaue的值就是undefined。

ps:从上图可以看到存取实例中的属性比存取原型中的属性效率要高,即obj2.value比obj1.value少一个指针访问....

JavaScript 原型继承开端,布布扣,bubuko.com

JavaScript 原型继承开端

上一篇:C++类的成员


下一篇:Python模块和包