参考:JavaScript Programming Patterns
本篇会介绍六种js编程模式:
- The Old-School Way
- Singleton
- Module Pattern
- Revealing Module Pattern
- Custom Objects
- Lazy Function Definition
问题场景:网页中有三个超链接,每点击一个,则将其背景色设为特定值,如下所示:
<a href="#">设置背景为红色</a> <a href="#">设置背景为绿色</a> <a href="#">设置背景为蓝色</a>
The Old-School Way
意图:(用最纯的方式实现,简单、直接!)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function
changeColor(linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
}
function
anchorChange() {
var
config = { colors: [
"red"
,
"green"
,
"blue"
] };
// 获取所有的标签
var
anchors = document.getElementsByTagName(
"a"
);
var
size = anchors.length;
// 遍历+加入click监听
for
(
var
i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick =
function
() {
changeColor(
this
,
this
.color);
return
false
;
};
}
}
|
使用方式:在所有的<a>标签渲染完成之后,加入
1
|
<script type=
"text/javascript"
> anchorChange(); </script>
|
效果见示例1,这样可以实现我们的需求,但这样做,最大的问题是什么???就是:所有的方法均为全局方法,你可以用全局变量来类比理解。所以,很严重的后果是:方法被覆盖!假设多人开发,如果有一同胞也写了一个方法(如下所示),则问题不言而喻!
1
2
3
|
function
changeColor() {
// 我自己写了一个方法,改变了字体颜色
}
|
那如何解决这个问题呢?上述问题的关键点在于方法的namespace上!那我们试试Singleton如何?
Singleton
意图:(对功能进行“对象”级别的封装是个不错的选择!)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
var
anchorChange = {
config: { colors: [
"red"
,
"green"
,
"blue"
] },
changeColor:
function
(linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
},
init:
function
() {
var
self =
this
;
var
anchors = document.getElementsByTagName(
"a"
);
var
size = anchors.length;
for
(
var
i = 0; i < size; i++) {
anchors[i].color = self.config.colors[i];
anchors[i].onclick =
function
() {
self.changeColor(
this
,
this
.color);
return
false
;
};
}
}
};
|
使用方式:在所有的<a>标签渲染完成之后,加入
1
|
<script type=
"text/javascript"
> anchorChange.init(); </script>
|
效果见示例二。哇!这样的确好了很多,但问题是还不够好!因为我们知道,面向对象的封装,是为了更好的保证对象的完整性。为什么要封装?目的就是为了合理的隐藏与公开,就像一个人,我封装了所有的内部结构,比如身体器官、血液、骨骼等,这些都是private的,而我只把胳膊、手提供给外界可视。这样的好处是什么??其实你这么想,如果外界可以直接访问你的骨骼、血液、器官,那出问题的机会岂不大发了??所以,封装是为了更好的保护我们的私有属性,减少错误调用,这些属性都是必不可少,但是只做为我们自己的内部服务,不需要外界直接访问,外界甚至根本不需要知道有这些属性。
那回顾头来看这个问题,我们并没有做好封装,因为就拿config属性来看,这就是不需要外界访问的私有域。为了解决这个问题,继续来看Module Pattern
Module Pattern
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
var
anchorChange =
function
() {
// private property
var
config = { colors: [
"red"
,
"green"
,
"blue"
] }
// this is a private method
// can be accessed within anchorChange
// cannot be accessed from outside
function
alterColor(linkObj, color) {
linkObj.style.backgroundColor = color;
}
return
{
// public method
// can be accessed from outside
changeColor:
function
(linkObj, newColor) {
// calls private function to change color
alterColor(linkObj, newColor);
},
// public method
// can be accessed from outside
init:
function
() {
var
self =
this
;
// assign reference to current object to "self"
// get all links on the page
var
anchors = document.getElementsByTagName(
"a"
);
var
size = anchors.length;
for
(
var
i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick =
function
() {
self.changeColor(
this
,
this
.color);
// this is bound to the anchor object
return
false
;
};
}
}
};
}();
|
效果见示例三。这个已经逐渐符合我们的预期啦,良好的封装让我们的游刃有余!额。。。稍等,好像略显复杂,目的明确,结构不清晰啊!有没有更直接、更简单的??千呼万唤始出来,Revealing Module Pattern登场!!!
Revealing Module Pattern
1
2
3
4
5
6
7
8
|
var
anchorChange =
function
() {
// private property
var
config = { colors: [
"red"
,
"green"
,
"blue"
] }
// this is a publicmethod
// can be accessed from outside
function
changeColor(linkObj, color) {
linkObj.style.backgroundColor = color;
}
// public method
// can be accessed from outside
init:
function
() {
var
self =
this
;
// assign reference to current object to "self"
// get all links on the page
var
anchors = document.getElementsByTagName(
"a"
);
var
size = anchors.length;
for
(
var
i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick =
function
() {
self.changeColor(
this
,
this
.color);
// this is bound to the anchor object
return
false
;
};
}
}
return
{
init : init,
changeColor : changeColor
};
}
();
|
Custom Objects
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// custom object constructor
var
anchorChanger =
function
() {
this
.init();
};
anchorChanger.prototype.config = { colors: [
"red"
,
"green"
,
"blue"
] }
anchorChanger.prototype.changeColor =
function
(linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
};
anchorChanger.prototype.init =
function
() {
var
self =
this
;
// get all links on the page
var
anchors = document.getElementsByTagName(
"a"
);
var
size = anchors.length;
for
(
var
i = 0; i < size; i++) {
anchors[i].color = self.config.colors[i];
anchors[i].onclick =
function
() {
self.changeColor(
this
,
this
.color);
return
false
;
};
}
};
|
效果见示例5.真心不错啊!但还有一个问题尚未解决,比如,我需要在changeColor()中进行大量的数学计算,花费较长的时间,那么,上述几种方案就显得美中不足啦,所以,提出Lazy Function Definition,是为了完善和补充以上应用场景,go on!
Lazy Function Definition
顾名思义,如同我们在设计模式之单例模式中提到的一样,提供一种“懒定义”,很好理解,懒就是不立刻或者提前去做,而是只有在需要的时候,才做,懒人么....
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
// lazy function definition
var
anchorChange =
function
() {
// define configuration
var
config = { colors: [
"red"
,
"green"
,
"blue"
] };
// get all links
var
anchors = document.getElementsByTagName(
"a"
);
var
size = anchors.length;
// loop through anchors and attach events
for
(
var
i = 0; i < size; i++) {
anchors[i].color = config.colors[i];
anchors[i].onclick =
function
() {
anchorChange5().changeColor(
this
,
this
.color);
return
false
;
};
}
// redefine function so that it only holds the changeColor function
anchorChange = function () {
return
{
changeColor:
function
(linkObj, newColor) {
linkObj.style.backgroundColor = newColor;
}
};
};
};
|
效果见示例6.