Effective JavaScript Item 36 实例状态只保存在实例对象上

本系列作为EffectiveJavaScript的读书笔记。

一个类型的prototype和该类型的实例之间是”一对多“的关系。那么,需要确保实例相关的数据不会被错误地保存在prototype之上。比如,对于一个实现了树结构的类型而言,将它的子节点保存在该类型的prototype上就是不正确的:

function Tree(x) {
	this.value = x;
}
Tree.prototype = {
	children: [], // should be instance state!
	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 top = new Tree(4);
top.addChild(left);
top.addChild(right);

top.children; // [1, 3, 5, 7, left, right]

当状态被保存到了prototype上时,所有实例的状态都会被集中地保存,在上面这种场景中显然是不正确的:本来属于每个实例的状态被错误地共享了。如下图所示:

正确的实现应该是这样的:

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

此时,实例状态的存储如下所示:

可见,当本属于实例的状态被共享到prototype上时,也许会产生问题。在需要在prototype上保存状态属性前,一定要确保该属性是能够被共享的。

总体而言,当一个属性是不可变(无状态)的属性时,就能将它保存在prototype对象上(比如方法能够被保存在prototype对象上就是因为这一点)。当然,有状态的属性也能够被放在prototype对象上,这要取决于具体的应用场景,典型的比如用来记录一个类型实例数量的变量。使用Java语言作为类比的话,这类能够存储在prototype对象上的变量就是Java中的类变量(使用static关键字修饰)。

总结:

  1. 当在prototype对象上存放可变数据时,可能会带来问题。
  2. 一般情况下,每个实例特有的可变属性需要被存放在实例本身上。
时间: 2024-10-08 23:45:47

Effective JavaScript Item 36 实例状态只保存在实例对象上的相关文章

Effective JavaScript Item 35 使用闭包来保存私有数据

本系列作为EffectiveJavaScript的读书笔记. JavaScript的对象系统从其语法上而言并不鼓舞使用信息隐藏(Information Hiding).由于当使用诸如this.name.this.passwordHash的时候,这些属性默认的訪问级别就是public的.在不论什么位置都可以通过obj.name,obj.passwordHash来对这些属性进行訪问. 在ES5环境中,也提供了一些方法来更方便的訪问一个对象上全部的属性,比方Object.keys(),Object.g

[Effective JavaScript 笔记]第51条:在类数组对象上复用通用的数组方法

前面有几条都讲过关于Array.prototype的标准方法.这些标准方法被设计成其他对象可复用的方法,即使这些对象并没有继承Array. arguments对象 在22条中提到的函数arguments对象.它是一个类数组对象,并不是一个标准的数组,所以无法使用数组原型中的方法,因此无法使用arguments.forEach这样的形式来遍历每一个参数.这里我们必须使用call方法来对使用forEach方法. function highlight(){ [].forEach.call(argume

Effective C++ Item 36 绝不重新定义继承而来的 non-virtual 函数

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:绝对不要重新定义继承而来的 non-virtual 函数 --> Item 7 "为多态基类声明 virtual 析构函数" 是本条款的特例 示例: class B{ public: void mf(); //... }; class D: public B{ public: void mf(); // 遮掩了B::mf,Item 33 名称遮掩规则 } D x; B

Effective JavaScript Item 24 使用一个变量来保存arguments的引用

本系列作为Effective JavaScript的读书笔记. 假设需要一个API用来遍历若干元素,像下面这样: var it = values(1, 4, 1, 4, 2, 1, 3, 5, 6); it.next(); // 1 it.next(); // 4 it.next(); // 1 相应的实现可以是: function values() { var i = 0, n = arguments.length; return { hasNext: function() { return

Effective JavaScript Item 34 在prototype上保存方法

本系列作为EffectiveJavaScript的读书笔记. 不使用prototype进行JavaScript的编码是完全可行的,例如: function User(name, passwordHash) { this.name = name; this.passwordHash = passwordHash; this.toString = function() { return "[User " + this.name + "]"; }; this.checkP

Effective JavaScript Item 37 认识this的隐式指向

本系列作为Effective JavaScript的读书笔记. CSV数据通常都会被某种分隔符进行分隔,所以在实现CSV Reader时,需要支持不同的分隔符.那么,很自然的一种实现就是将分隔符作为构造函数的参数. function CSVReader(separators) { this.separators = separators || [","]; this.regexp = new RegExp(this.separators.map(function(sep) { retu

Effective JavaScript Item 38 调用父类的构造函数在子类的构造函数

作为这一系列Effective JavaScript的读书笔记. 在一个游戏或者图形模拟的应用中.都会有场景(Scene)这一概念.在一个场景中会包括一个对象集合,这些对象被称为角色(Actor). 而每一个角色依据其类型会有一个图像用来表示,同一时候场景也须要保存一个底层图形展示对象的引用,被称为上下文(Context): function Scene(context, width, height, images) { this.context = context; this.width =

Effective JavaScript Item 38 在子类构造函数中调用父类构造函数

本系列作为Effective JavaScript的读书笔记. 在一个游戏或者图形模拟的应用中,都会有场景(Scene)这一概念.在一个场景中会包含一个对象集合,这些对象被称为角色(Actor).而每个角色根据其类型会有一个图像用来表示,同时场景也需要保存一个底层图形展示对象的引用,被称为上下文(Context): function Scene(context, width, height, images) { this.context = context; this.width = width

Effective JavaScript Item 23 永远不要修改arguments对象

本系列作为Effective JavaScript的读书笔记. arguments对象只是一个类似数组的对象,但是它并没有数组对象提供的方法,比如shift,push等.因此调用诸如:arguments.shift(),arguments.push()是错误的. 在Item 20和Item 21中,知道了函数对象上存在call和apply方法,那么是不是可以利用它们来让arguments也能够利用数组的方法呢: function callMethod(obj, method) { var shi