Skip to content

Commit 4251240

Browse files
committed
单例模式 校对完毕
1 parent 599b16c commit 4251240

File tree

1 file changed

+53
-61
lines changed

1 file changed

+53
-61
lines changed

chapter7.markdown

Lines changed: 53 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
1-
<a name="a1"></a>
21
# 设计模式
32

43
在GoF(Gang of Four)的书中提出的设计模式为面向对象的软件设计中遇到的一些普遍问题提供了解决方案。它们已经诞生很久了,而且被证实在很多情况下是很有效的。这正是你需要熟悉它的原因,也是我们要讨论它的原因。
54

65
尽管这些设计模式跟语言和具体的实现方式无关,但它们多年来被关注到的方面仍然主要是在强类型静态语言比如C++和Java中的应用。
76

8-
JavaScript作为一种基于原型的弱类型动态语言,使得有些时候实现某些模式时相当简单,甚至不费吹灰之力。
7+
JavaScript作为一种基于原型的弱类型动态语言,有些时候实现某些模式时相当简单,甚至不费吹灰之力。
98

109
让我们从第一个例子——单例模式——来看一下在JavaScript中和静态的基于类的语言有什么不同。
1110

12-
<a name="a2"></a>
1311
## 单例
1412

1513
单例模式的核心思想是让指定的类只存在唯一一个实例。这意味着当你第二次使用相同的类去创建对象的时候,你得到的应该和第一次创建的是同一个对象。
@@ -28,143 +26,138 @@ JavaScript作为一种基于原型的弱类型动态语言,使得有些时候
2826
obj === obj2; // false
2927
obj == obj2; // false
3028

31-
所以你可以说当你每次使用对象字面量创建一个对象的时候就是在创建一个单例,并没有特别的语法迁涉进来
29+
所以你可以说当你每次使用对象字面量创建一个对象的时候就是在创建一个单例,并没有什么特别的语法牵涉进来
3230

33-
> 需要注意的是,有的时候当人们在JavaScript中提出“单例”的时候,它们可能是在指第5章讨论过的“模块模式”。
31+
> 需要注意的是,有的时候当人们在JavaScript中提出“单例”的时候,它们可能是在指第五章讨论过的“模块模式”。
3432
35-
<a name="a3"></a>
3633
### 使用new
3734

38-
JavaScript没有类,所以一字一句地说单例的定义并没有什么意义。但是JavaScript有使用new、通过构造函数来创建对象的语法,有时候你可能需要这种语法下的一个单例实现。这也就是说当你使用new、通过同一个构造函数来创建多个对象的时候,你应该只是得到同一个对象的不同引用。
35+
JavaScript没有类,所以一字一句地说单例的定义并没有什么意义。但是JavaScript有使用`new`、通过构造函数来创建对象的语法,有时候你可能需要这种语法下的一个单例实现。这也就是说当你使用`new`、通过同一个构造函数来创建多个对象的时候,你应该只是得到同一个对象的不同引用。
3936

40-
> 温馨提示:从一个实用模式的角度来说,下面的讨论并不是那么有用,只是更多地在实践模拟一些语言中关于这个模式的一些问题的解决方案。这些语言主要是(静态强类型的)基于类的语言,在这些语言中,函数并不是“一等公民”。
37+
> 温馨提示:从一个实用模式的角度来说,下面的讨论并不是那么有用,只是更多地在模拟一些语言中关于这个模式的一些问题的解决方案。这些语言主要是(静态强类型的)基于类的语言,在这些语言中,函数并不是“一等公民”。
4138
4239
下面的代码片段展示了期望的结果(假设你忽略了多元宇宙的设想,接受了只有一个宇宙的观点):
4340

4441
var uni = new Universe();
4542
var uni2 = new Universe();
4643
uni === uni2; // true
4744

48-
在这个例子中,uni只在构造函数第一次被调用时创建。第二次(以及后续更多次)调用时,同一个uni对象被返回。这就是为什么uni === uni2的原因——因为它们实际上是同一个对象的两个引用。那么怎么在JavaScript达到这个效果呢?
45+
在这个例子中,`uni`只在构造函数第一次被调用时创建。第二次(以及后续更多次)调用时,同一个`uni`对象被返回。这就是为什么`uni === uni2`的原因——因为它们实际上是同一个对象的两个引用。那么怎么在JavaScript达到这个效果呢?
4946

50-
当对象实例this被创建时,你需要在Universe构造函数中缓存它,以便在第二次调用的时候返回。有几种选择可以达到这种效果:
47+
当对象实例`this`被创建时,你需要在`Universe()`构造函数中缓存它,以便在第二次调用的时候返回。有几种选择可以达到这种效果:
5148

5249
- 你可以使用一个全局变量来存储实例。不推荐使用这种方法,因为通常我们认为使用全局变量是不好的。而且,任何人都可以改写全局变量的值,甚至可能是无意中改写。所以我们不再讨论这种方案。
53-
- 你也可以将对象实例缓存在构造函数的属性中。在JavaScript中,函数也是对象,所以它们也可以有属性。你可以写一些类似Universe.instance的属性来缓存对象。这是一种漂亮干净的解决方案,不足之处是instance属性仍然是可以被公开访问的,别人写的代码可能修改它,这样就会失去这个实例。
50+
- 你也可以将对象实例缓存在构造函数的属性中。在JavaScript中,函数也是对象,所以它们也可以有属性。你可以写一些类似`Universe.instance`的属性来缓存对象。这是一种漂亮干净的解决方案,不足之处是`instance`属性仍然是可以被公开访问的,别人写的代码可能修改它,这样就会失去这个实例。
5451
- 你可以将实例包裹在闭包中。这可以保持实例是私有的,不会在构造函数之外被修改,代价是一个额外的闭包。
5552

5653
让我们来看一下第二种和第三种方案的实现示例。
5754

58-
<a name="a4"></a>
5955
### 将实例放到静态属性中
6056

61-
下面是一个将唯一的实例放入Universe构造函数的一个静态属性中的例子
57+
下面是一个将唯一的实例放入`Universe()`构造函数的一个静态属性中的例子
6258

6359
function Universe() {
6460

65-
// do we have an existing instance?
66-
if (typeof Universe.instance === "object") {
67-
return Universe.instance;
68-
}
69-
70-
// proceed as normal
71-
this.start_time = 0;
72-
this.bang = "Big";
73-
74-
// cache
75-
Universe.instance = this;
76-
77-
// implicit return:
78-
// return this;
61+
// 实例是否已经存在?
62+
if (typeof Universe.instance === "object") {
63+
return Universe.instance;
64+
}
65+
66+
// 处理普通逻辑
67+
this.start_time = 0;
68+
this.bang = "Big";
69+
70+
// 缓存实例
71+
Universe.instance = this;
72+
73+
// 隐式return:
74+
// return this;
7975
}
8076

81-
// testing
77+
// 测试
8278
var uni = new Universe();
8379
var uni2 = new Universe();
8480
uni === uni2; // true
8581

86-
如你所见,这是一种直接有效的解决方案,唯一的缺陷是instance是可被公开访问的。一般来说它被其它代码误删改的可能是很小的(起码比全局变量instance要小得多),但是仍然是有可能的。
82+
如你所见,这是一种直接有效的解决方案,唯一的缺陷是`instance`是可被公开访问的。一般来说它被其它代码误删改的可能是很小的(起码比全局变量`instance`要小得多),但是仍然是有可能的。
8783

88-
<a name="a5"></a>
8984
### 将实例放到闭包中
9085

91-
另一种实现基于类的单例模式的方法是使用一个闭包来保护这个唯一的实例。你可以通过第5章讨论过的“私有静态成员模式”来实现。唯一的秘密就是重写构造函数:
86+
另一种实现基于类的单例模式的方法是使用一个闭包来保护这个唯一的实例。你可以通过第五章讨论过的“私有静态成员模式”来实现。唯一的秘密就是重写构造函数:
9287

9388
function Universe() {
9489

95-
// the cached instance
90+
// 缓存实例
9691
var instance = this;
9792
98-
// proceed as normal
93+
// 处理普通逻辑
9994
this.start_time = 0;
10095
this.bang = "Big";
10196
102-
// rewrite the constructor
97+
// 重写构造函数
10398
Universe = function () {
10499
return instance;
105100
};
106101
}
107102

108-
// testing
103+
// 测试
109104
var uni = new Universe();
110105
var uni2 = new Universe();
111106
uni === uni2; // true
112107

113-
第一次调用时,原始的构造函数被调用并且正常返回this。在后续的调用中,被重写的构造函数被调用。被重写怕这个构造函数可以通过闭包访问私有的instance变量并且将它返回
108+
第一次调用时,原来的构造函数被调用并且正常返回`this`。在后续的调用中,被重写的构造函数被调用。被重写的这个构造函数可以通过闭包访问私有的`instance`变量并且将它返回
114109

115-
这个实现实际上也是第4章讨论的自定义函数的又一个例子。如我们讨论过的一样,这种模式的缺点是被重写的函数(在这个例子中就是构造函数Universe())将丢失那些在初始定义和重新定义之间添加的属性。在这个例子中,任何添加到Universe()的原型上的属性将不会被链接到使用原来的实现创建的实例上。(注:这里的“原来的实现”是指实例是由未被重写的构造函数创建的,而Universe()则是被重写的构造函数。)
110+
这个实现实际上也是第四章讨论的重定义函数的又一个例子。如我们讨论过的一样,这种模式的缺点是被重写的函数(在这个例子中就是构造函数`Universe()`)将丢失那些在初始定义和重新定义之间添加的属性。在这个例子中,任何添加到`Universe()`的原型上的属性将不会被链接到使用原来的实现创建的实例上。(注:这里的“原来的实现”是指实例是由未被重写的构造函数创建的,`Universe()`则是被重写的构造函数。)
116111

117112
下面我们通过一些测试来展示这个问题:
118113

119-
// adding to the prototype
114+
// 添加成员到原型
120115
Universe.prototype.nothing = true;
121116

122117
var uni = new Universe();
123118

124-
// again adding to the prototype
125-
// after the initial object is created
119+
// 在创建一个对象后再添加成员到原型
126120
Universe.prototype.everything = true;
127121

128122
var uni2 = new Universe();
129123

130-
Testing:
131-
// only the original prototype was
132-
// linked to the objects
124+
// 测试:
125+
// 只有原始的原型被链接到对象上
133126
uni.nothing; // true
134127
uni2.nothing; // true
135128
uni.everything; // undefined
136129
uni2.everything; // undefined
137130

138-
// that sounds right:
131+
// constructor看起来是对的
139132
uni.constructor.name; // "Universe"
140133

141-
// but that's odd:
134+
// 但其实不然
142135
uni.constructor === Universe; // false
143136

144-
uni.constructor不再和Universe()相同的原因是uni.constructor仍然是指向原来的构造函数,而不是被重新定义的那个。
137+
`uni.constructor`不再和`Universe()`相同的原因是`uni.constructor`仍然是指向原来的构造函数,而不是被重新定义的那个。
145138

146-
如果一定被要求让prototype和constructor的指向像我们期望的那样,可以通过一些调整来做到:
139+
如果一定要让`prototype``constructor`的指向像我们期望的那样,可以通过一些调整来做到:
147140

148141
function Universe() {
149142

150-
// the cached instance
143+
// 缓存实例
151144
var instance;
152145
153-
// rewrite the constructor
146+
// 重写构造函数
154147
Universe = function Universe() {
155148
return instance;
156149
};
157150
158-
// carry over the prototype properties
151+
// 重写prototype属性
159152
Universe.prototype = this;
160153
161-
// the instance
154+
// 创建实例
162155
instance = new Universe();
163156
164-
// reset the constructor pointer
157+
// 重写constructor属性
165158
instance.constructor = Universe;
166159
167-
// all the functionality
160+
// 其它的功能代码
168161
instance.start_time = 0;
169162
instance.bang = "Big";
170163
@@ -173,24 +166,23 @@ uni.constructor不再和Universe()相同的原因是uni.constructor仍然是指
173166

174167
现在所有的测试结果都可以像我们期望的那样了:
175168

176-
// update prototype and create instance
169+
// 修改原型,创建对象
177170
Universe.prototype.nothing = true; // true
178171
var uni = new Universe();
179172
Universe.prototype.everything = true; // true
180173
var uni2 = new Universe();
181174

182-
// it's the same single instance
175+
// 它们是同一个实例
183176
uni === uni2; // true
184177

185-
// all prototype properties work
186-
// no matter when they were defined
178+
// 所有的原型上的属性都正常工作,不管是什么时候在哪添加的
187179
uni.nothing && uni.everything && uni2.nothing && uni2.everything; // true
188-
// the normal properties work
180+
// 普通成员也可以正常工作
189181
uni.bang; // "Big"
190-
// the constructor points correctly
182+
// constructor指向正确
191183
uni.constructor === Universe; // true
192184

193-
另一种可选的解决方案是将构造函数和实例包在一个立即执行的函数中。当构造函数第一次被调用的时候,它返回一个对象并且将私有的instance指向它。在后续调用时,构造函数只是简单地返回这个私有变量。在这种新的实现下,前面所有的测试代码也会和期望的一样:
185+
另一种可选的解决方案是将构造函数和实例包在一个即时函数中。当构造函数第一次被调用的时候,它返回一个对象并且将私有的`instance`指向它。在后续调用时,构造函数只是简单地返回这个私有变量。在这种新的实现下,前面所有的测试代码也会和期望的一样:
194186

195187
var Universe;
196188

@@ -206,7 +198,7 @@ uni.constructor不再和Universe()相同的原因是uni.constructor仍然是指
206198
207199
instance = this;
208200
209-
// all the functionality
201+
// 功能代码
210202
this.start_time = 0;
211203
this.bang = "Big";
212204

0 commit comments

Comments
 (0)