Javascript 设计模式读书笔记

一直很喜欢前端界的大牛addyosmani,趁着假期拜读了他的Javascript设计模式Learning Javascript Design Patterns。这篇文章主要作为个人在javascript底层知识的学习记录和笔记。其实今年来陆陆续续有在关注和看一下国外的书籍及前沿的技术内容,但确实是感觉自己的英文有点菜,看不懂的内容很多,尤其是对于长句子的解读更加弱爆。这方面的动力主要是工作压力原因和个人的意愿,以我目前的知识结构感觉很难让自己及整个团队再上一个台阶。

什么是设计模式

设计模式3个最主要的好处:

  • 被验证的解决方案
  • 能够被重用
  • 富有表现张力

反模式

  • 定义大量的全局变量
  • 使用setTimeout或setInterval内部用eval触发的字符串
    错误用法
    1
    2
    setTimeout("myFunc()", 1000);
    setTimeout("myFunc(1,2,3)", 1000);

正确用法

1
2
3
4
setTimeout(myFunc, 1000);
setTimeout(function () {
myFunc(1, 2, 3);
});

  • 修改JS原生的对象原型
  • 直接在一行里面写JS代码
  • 使用document.createElement替代document.write

设计模式分类

创建型设计模式

集中处理对象创建机制,控制创建过程
Constructor, Factory, Abstract, Prototype, Singleton, Builder.

结构设计模式

关注对象构成及定义简单的方式实现不同对象的关系
Decorator, Facade, Flyweight, Adapter, Proxy

行为设计模式

改进或提高系统里不同对象的通讯
Iterator, Mediator, Observer, Visitor.

JS设计模式

原生的对象

这里看到有些中文书籍硬生生地把Object literal notation翻译成对象字面量,感觉非常地奇怪。个人更愿意把它叫做Javascript原生对象。

1
2
3
4
5
6
7
var myObject = {
key: 'value';
func: function() {
// ...
}
}
myObject.key2 = 'new value';

模块模式(Module Pattern)

模块模式一般是着重于类公有/私有的封装,在Javascript里模块是用一个包含着公有/私有方法和变量的简单对象来模拟。模块模式的封装主要是用闭包,保证只返回公有方法,其他内容保存在闭包里面。
这里还有一个叫IIFE的概念。全称是immediately-invoked functional expression. 立即调用函数表达式。通过IIFE实现闭包。Javascript不像传统的其他面向对象语言拥有公有/私有变量的语法,所以通过函数作用域来模拟,除了在返回对象里面定义的方法和变量对外公开,其他变量和方法都属于模块自己私有。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var testModule = function() {

var counter = 0;

return {
incrementCounter: function() {
return counter++;
},

resetCounter: function() {
counter = 0;
}
}

})();

模块暴露模式(The Revealing Module Pattern)

模块暴露模式其实是做为模块模式的补充,这里这个翻译不知道是不是最合适的。这种js的写法现在已经被大规模的推广,个人感觉比较好的地方就是在代码尾段的返回对象就可以对这个模块对外的接口一目了然。当然这种情况也更多的使用在类似一些until工具类上。但书里提到的关于2者的区别其实界限我没看得太明白,在此暂时搁下再议。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var revealModule = (function() {

var privateVar = 'real module',
publicVar = 'hello';

function privateDisplay() {
console.log('my name:' + privateVar);
}

function publicSetName(name) {
privateVar = name;
}

function publicGetName() {
return privateDisplay();
}

return {
setName: publicSetName,
getName: publicGetName,
greeting: publicVar,
}
})();

单例模式

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var mySingleton = (function() {

var instance;

function init() {
return {
publicMethod: function() {
console.log('singleton');
}
};
}

return {
getInstance: function() {
if (!instance) {
instance = init();
}

return instacne;
}
}
})();
mySingleton.getInstance().publicMethod();

观察者模式

目前比较流行的Publish/Subscribe模式,

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
var pubsub = {};
(function (q) {
var topis = {},
subUid = -1;

// 事件广播(主题名和参数)
q.publish = function (topic, args) {
if (!topics[topic]) {
return false;
}

var subscribers = topics[topic],
len = subscribers ? subscribers.length : 0;

while (len--) {
subscribers[len].func(topic, args);
}
return this;
}

// 订阅事件(主题名和回调函数)
q.suscribe = function (topic, func) {

if (!topic[topic]) {
topics[topic] = [];
}

var token = (++subUid).toString();
topics[topic].push({
token: token,
func: func
});
return token;
}

q.unsubscribe = function (token) {
for (var m in topics) {
if (topics[m]) {
for (var i = 0, j = topics[m].length; i < j; i++) {
if (topics[m][i].token == token) {
topics[m].splice(i, 1);
return token;
}
}
}
}
return this;
}

}(pubsub));

中介者模式

指令模式