理解原型对象与其实例之间是一对多的关系,对于实现正确的对象行为很重要。
常见的错误是不小心将每个实例的数据存储到了其原型中。
示例
一个实现了树型数据结构的类可能将子节点存储在数组中。
实例状态在原型中(错误)
将存储子节点的数组放置在原型对象中将会导致实现被完全破坏。
function Tree(x){
this.value=x;
}
Tree.prototype={
children:[],
addChild:function(x){
this.children.push(x);
}
}
如果我们使用这个类构造一棵树,代码如下
var left=new Tree(2);
left.addChild(1);
left.addChild(3);
var right=new Tree(6);
right.addChild(5);
right.addChild(7);
var main=new Tree(4);
main.addChild(left);
main.addChild(right);
main.children;//[1,3,5,7,left,right]
我们预想的main.children应该是只包括left,right两个Tree的实例的,为什么包括这些了呢?
看一下下面这张图就明白了
这里面原有的实例都要可以访问和操作原型中children,然后都是对这个数组进行了添加,导致对应实例的children数据出现错误。
实例状态在实例对象中(正确)
实现Tree类的正确方式是为每个实例创建一个单独的children数组。
function Tree(x){
this.value=x;
this.children=[];
}
Tree.prototype={
addChild:function(){
this.children.push(x);
}
}
分析
共享有状态的数据可能会导致错误。通常在一个类的多个实例之间共享方法是安全的,因为方法通常是无状态的,这不同于通过this来引用实例状态。(因为方法调用的语法确保了this被绑定到实例对象,即使该方法是从原型中继承来的,共享方法仍然可以访问实例状态)一般情况下,任何不可变的数据可以被存储在原型中从而被安全地共享。有状态的数据原则上也可以存储在原型中,只要你真正想共享它。在原型中最常见的数据是方法,而每个实例的状态都存储在实例对象中。
提示
-
共享可变数据可能会出现问题,因为原型是被其所有的实例共享的
-
将可变的实例状态存储在实例对象中