1.原型继承本质
就javascript对象系统的实现来讲,对象并没有原型,而构造器有原型(构造器.prototype指向其原型)。对象只有构造自某个原型的说法,并没有持有某个原型的说法。原型其实也是一个对象实例。原型的含义是指:如果构造器有一个原型对象A,则由该构造器创建的实例都必然复制自A。
2.空的对象是所有对象的基础
来看看以下代码:
<html>
<head>
<meta http-equiv="content-type" charset="utf-8"/>
<script type="text/javascript">
var proto = Object.prototype; var count = 0;
for(var n in proto)
{
count++;
}
alert(count);//return 0;
</script>
</head>
<body>
</body>
</html>
由上边代码我们知道Object()构造器的原型就是一个空的对象。下边的两行代码无非就是从Object.prototype复制出的一个映像,也是空的对象
obj1 = new Object();
obj2 = {};
构建过程可以简单理解为”复制”
图 2.1 对象构建
3.构造复制---写复制---读遍历
由图 2.1可以看出,每构造出一个实例,都需要从原型中复制出一个相应的实例出来,但这种构造复制会产生一个问题:内存空间的急剧消耗。
因此我们的另一个策略就是写时复制,先看下图:
图 3.1 写时复制
在系统中只要将obj1和obj2的引用指向对象的原型,这样在读取的时候顺着引用读取即可。只有当需要修改对象obj1或obj2的属性时才复制一个映像出来,以后就操作该映像。写时复制的优点是只在第一次写的时候用一些代码来分配内存,带来一些代码和内存上的开销,以后就没有这种开销了。但如果需要经常写操作,则这种方式与上一种没有什么区别。
第三种策略就是把写复制的粒度再次变小,从原型变成成员,仅当写某个实例的成员时,将成员信息复制到实例映像中。
图 3.2 成员复制
我们发现obj2仍旧指向原型,在写操作时也没有与原型相同大小的实例被创建出来,不会导致大量的内存分配,因此在内存使用上更加经济。但obj2需要维护一张成员列表,列表中存放修改了的成员,列表中的成员是否与原型中的成员是否一致并不重要,只需要遵循两条规则。
1)保证读取时先被访问到。我们访问obj2.value时,得到10.但对于obj1.value,这时读遍历规则就起作用了:
2)如果对象中没有指定属性,则会尝试遍历对象的整个原型链,知道找到該值或者为空。
假设原型也持有一张成员列表,如下:
图 3.3 读遍历
那么,obj2.value为10,而obj1.value自然为’abc’。我们也发现原型带来的另外一个效果:obj1.value与obj2.value的类型并不相同。如果原型链上没有维护value这个成员,那么obj1.vlaue的值就是undefined。
ps:从上图可以看到存取实例中的属性比存取原型中的属性效率要高,即obj2.value比obj1.value少一个指针访问....