你的位置:首页 > Java教程

[Java教程][Effective JavaScript 笔记]第36条:只将实例状态存储在实例对象中


理解原型对象与其实例之间是一对多的关系,对于实现正确的对象行为很重要。
常见的错误是不小心将每个实例的数据存储到了其原型中。

示例

一个实现了树型数据结构的类可能将子节点存储在数组中。

实例状态在原型中(错误)

将存储子节点的数组放置在原型对象中将会导致实现被完全破坏。

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的实例的,为什么包括这些了呢?
看一下下面这张图就明白了
1466039875998
这里面原有的实例都要可以访问和操作原型中children,然后都是对这个数组进行了添加,导致对应实例的children数据出现错误。

实例状态在实例对象中(正确)

实现Tree类的正确方式是为每个实例创建一个单独的children数组。

function Tree(x){ this.value=x; this.children=[];}Tree.prototype={ addChild:function(){  this.children.push(x); }}

同样的我们看一张上面代码的图示
1466039901118

分析

共享有状态的数据可能会导致错误。通常在一个类的多个实例之间共享方法是安全的,因为方法通常是无状态的,这不同于通过this来引用实例状态。(因为方法调用的语法确保了this被绑定到实例对象,即使该方法是从原型中继承来的,共享方法仍然可以访问实例状态)一般情况下,任何不可变的数据可以被存储在原型中从而被安全地共享。有状态的数据原则上也可以存储在原型中,只要你真正想共享它。在原型中最常见的数据是方法,而每个实例的状态都存储在实例对象中。

提示

  • 共享可变数据可能会出现问题,因为原型是被其所有的实例共享的

  • 将可变的实例状态存储在实例对象中