Bermu

探索JavaScript的设计模式

2018-08-06

单例模式

保证一个类只有一个实例,提供一个唯一的访问点来暴露该对象。

1
2
3
4
5
6
7
var mySingleton = {
property1: "something",
property2: "something else",
method1: function () {
console.log('hello world');
}
};
  • 一般用于各种模式的通信间

构造函数模式

在js中没有类的概念,但是有特殊的构造函数,通过new关键字来调用定义的函数,在构造函数内部通过this引用属性方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function Car(model, year, miles) {
this.model = model;
this.year = year;
this.miles = miles;
this.output= function () {
return this.model + "走了" + this.miles + "公里";
};
}

var tom= new Car("大叔", 2009, 20000);
var dudu= new Car("Dudu", 2010, 5000);

console.log(tom.output());
console.log(dudu.output());

建造者模式

将一个复杂对象的构建与其表示相分离,提供一种“封装机制”来区分出对象中复杂部分的变换。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function getBeerById(id, callback) {
// 使用ID来请求数据,然后返回数据.
asyncRequest('GET', 'beer.uri?id=' + id, function (resp) {
// callback调用 response
callback(resp.responseText);
});
}

var el = document.querySelector('#test');
el.addEventListener('click', getBeerByIdBridge, false);

function getBeerByIdBridge(e) {
getBeerById(this.id, function (beer) {
console.log('Requested Beer: ' + beer);
});
}

仔细观察也就是我们经常使用的互相调用。


工厂模式

工厂一般用于定义对象的接口,而接口一般由子类决定实例化。

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
var page = page || {};
page.dom = page.dom || {};
//子函数1:处理文本
page.dom.Text = function () {
this.insert = function (where) {
var txt = document.createTextNode(this.url);
where.appendChild(txt);
};
};

//子函数2:处理链接
page.dom.Link = function () {
this.insert = function (where) {
var link = document.createElement('a');
link.href = this.url;
link.appendChild(document.createTextNode(this.url));
where.appendChild(link);
};
};

//子函数3:处理图片
page.dom.Image = function () {
this.insert = function (where) {
var im = document.createElement('img');
im.src = this.url;
where.appendChild(im);
};
};

装饰者模式

旨创建一个需要被装饰的类,通过在子类中添加不同的行为进而达到实现影响原有类实例的模式,其实是一种实现继承的变体。

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
//需要装饰的类(函数)
function Macbook() {
this.cost = function () {
return 1000;
};
}

function Memory(macbook) {
this.cost = function () {
return macbook.cost() + 75;
};
}

function BlurayDrive(macbook) {
this.cost = function () {
return macbook.cost() + 300;
};
}


function Insurance(macbook) {
this.cost = function () {
return macbook.cost() + 250;
};
}


// 用法
var myMacbook = new Insurance(new BlurayDrive(new Memory(new Macbook())));
console.log(myMacbook.cost());

外观模式

为子系统中的一组接口提供了一个一致的界面,简化类中的接口。
(一个接口中包含了需要调用的业务接口)。

1
2
3
4
5
6
7
8
var mobileEvent = {
// ...
stop: function (e) {
e.preventDefault();
e.stopPropagation();
}
// ...
};

代理模式

顾名思义,需要有一份中间人去转办当事人之间的业务职能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 先声明美女对象
var girl = function (name) {
this.name = name;
};

// 这是dudu
var dudu = function (girl) {
this.girl = girl;
this.sendGift = function (gift) {
alert("Hi " + girl.name + ", dudu送你一个礼物:" + gift);
}
};

// 大叔是代理
var proxyTom = function (girl) {
this.girl = girl;
this.sendGift = function (gift) {
(new dudu(girl)).sendGift(gift); // 替dudu送花咯
}
};

// 调用
var proxy = new proxyTom(new girl("酸奶小妹"));
proxy.sendGift("999朵玫瑰");

观察者模式

也称发布/订阅模式,为当代MVVM框架中最为广泛传播的设计模式,它定义了一种一对多的关系,当系统有状态更改时,使得它们能够自动更新自身。

  • 支持简单的广播通信,自动通知所有已经订阅过的对象。
  • 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。[双绑机制]
  • 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
var pubsub = {};
(function (q) {

var topics = {}, // 回调函数存放的数组
subUid = -1;
// 发布方法
q.publish = function (topic, args) {

if (!topics[topic]) {
return false;
}

setTimeout(function () {
var subscribers = topics[topic],
len = subscribers ? subscribers.length : 0;

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

return true;

};
//订阅方法
q.subscribe = function (topic, func) {

if (!topics[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 false;
};
} (pubsub));



// 进行订阅与发布
//来,订阅一个
pubsub.subscribe('example1', function (topics, data) {
console.log(topics + ": " + data);
});

//发布通知
pubsub.publish('example1', 'hello world!');
pubsub.publish('example1', ['test', 'a', 'b', 'c']);
pubsub.publish('example1', [{ 'color': 'blue' }, { 'text': 'hello'}]);

或者是通过原型来创建。

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
function Observer() {
this.fns = [];
}
Observer.prototype = {
subscribe: function (fn) {
this.fns.push(fn);
},
unsubscribe: function (fn) {
this.fns = this.fns.filter(
function (el) {
if (el !== fn) {
return el;
}
}
);
},
update: function (o, thisObj) {
var scope = thisObj || window;
this.fns.forEach(
function (el) {
el.call(scope, o);
}
);
}
};

//测试
var o = new Observer;
var f1 = function (data) {
console.log('Robbin: ' + data + ', 赶紧干活了!');
};

var f2 = function (data) {
console.log('Randall: ' + data + ', 找他加点工资去!');
};

o.subscribe(f1);
o.subscribe(f2);

o.update("Tom回来了!")

//退订f1
o.unsubscribe(f1);
//再来验证
o.update("Tom回来了!");

策略模式

定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。

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
var validator = {

// 所有可以的验证规则处理类存放的地方,后面会单独定义
types: {},

// 验证类型所对应的错误消息
messages: [],

// 当然需要使用的验证类型
config: {},
}

// 验证给定的值是否不为空
validator.types.isNonEmpty = {
validate: function (value) {
return value !== "";
},
instructions: "传入的值不能为空"
};

// 验证给定的值是否是数字
validator.types.isNumber = {
validate: function (value) {
return !isNaN(value);
},
instructions: "传入的值只能是合法的数字,例如:1, 3.14 or 2010"
};

// 验证给定的值是否只是字母或数字
validator.types.isAlphaNum = {
validate: function (value) {
return !/[^a-z0-9]/i.test(value);
},
instructions: "传入的值只能保护字母和数字,不能包含特殊字符"
};

类似不同的验证目的通过不同的验证算法实现。

迭代器模式

提供一种方法顺序一个聚合对象中各个元素,而又不暴露该对象内部表示。

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
var agg = (function () {
var index = 0,
data = [1, 2, 3, 4, 5],
length = data.length;

return {
next: function () {
var element;
if (!this.hasNext()) {
return null;
}
element = data[index];
index = index + 2;
return element;
},

hasNext: function () {
return index < length;
},

rewind: function () {
index = 0;
},

current: function () {
return data[index];
}

};
} ());

就到这里吧!

使用支付宝打赏
使用微信打赏

若你觉得我的文章对你有帮助,欢迎点击上方按钮对我打赏

扫描二维码,分享此文章