# JS设计模式
JS设计模式 以及 原则
TIP
JS设计原则(SOLID)
S(single)单一职责原则:函数的
功能单一
。O(open-close)开放-封闭原则:对
扩展
开放,对修改
封闭的(增加需求时,扩展新代码,而非修改已有代码)。L(liskov)里氏替换原则:子类
可以代替父类
。父类能出现的地方,子类就能出现。I(interface)接口独立原则:接口的
功能单一
。D(dependence)依赖倒置原则:只依赖抽象接口,
不必关注具体类的实现
。
# 工厂模式
内部包装好一个对象,然后返回。(相当于工厂、车间)
function addPerson(name, age) {
var obj = new Object();
obj.name = name;
obj.age = agge;
obj.sayName = function() {
console.log(this.name)
}
}
const person1 = addPerson('heshiyu', 25);
const person2 = addPerson('zhouxingchi', 26)
2
3
4
5
6
7
8
9
10
11
应用场景:Reat.createElement、Vue.component异步组件等
# 单例模式
保证一个类仅有一个实例。
class Single {
login() {}
}
Single.getInstance = (function() {
let instance;
return function() {
if (!instance) {
instance = new Single()
}
return instance;
}
})() // 利用闭包,将变量instance一直存在内存中(直到Single.getInstance为null)
const obj1 = Single.getInstance();
const obj2 = Single.getInstance();
console.log(obj1 === obj2); // true
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
应用场景:Vuex中的Store、Redux中的Store、jQuery中的$
# 观察者模式
每个 被监听者 直接维护着它的 监听者列表。
- ① 在
Subject对象
里维护一个“观察者列表” - ② 实例化
Observer对象
时,指定要监听的Subject对象
- ② 当
Subject对象
的状态发生改变时,会通知每一个依赖它的Observers对象
class Subject {
constructor() {
this.state = 0;
this.observers = [];
}
// 取值
getState() {
return this.state;
}
// 设置值
setState(state) {
this.state = state;
this.notify(); // 状态发生改变,通知依赖
}
// 通知依赖
notify() {
this.observers.forEach(observer => observer.update());
}
// 添加依赖
add(observer) {
this.observer.push(observer);
}
}
class Observer {
constructor(name, subject) {
this.name = name;
this.subject = subject;
this.subject.add(this)
}
update() {
console.log(`${this.name}收到:得到更新${this.subject.getState()}`)
}
}
let sub = new Subject(); // 实例化一个Subject对象
let observer1 = new Observer('观察者1号', sub); // 实例化观察者时,指定哪个Subject
let observer2 = new Observer('观察者2号', sub);
sub.setState(1); // Subject对象更新
// 输出:
// 观察者1号收到:得到更新1
// 观察者2号收到:得到更新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
Vue watch、NodeJS自定义事件
# 发布-订阅模式
和观察者模式非常相似,但是最大的区别在于:
在发布-订阅模式,发布者(publishers)不会直接将消息发送给特定的订阅者
var Event = (function(){
var clientList = {}, // 订阅者列表
listen, // 订阅
trigger, // 触发通知
remove; // 移除订阅
listen = function(key, fn) {
if(!clientList[key]){
clientList[key] = []
}
clientList[key].push(fn)
}
trigger = function() {
var key = Array.prototype.shift.call(arguments)
fns = clientList[key]
if(!fns || fns.length === 0) {
return false
}
for(var i = 0, fn; fn = fns[i++];) {
fn.apply(this, arguments)
}
}
remove = function(key, fn) {
var fns = clientList[key]
if(!fns) {
return false
}
if(!fn){
fns && (fns.length = 0)
} else {
for(var l = fns.length - 1; l >= 0; l--) {
var _fn = fns[l]
if (_fn === fn) {
fns.splice(l, 1)
}
}
}
}
return {
listen,
trigger,
remove
}
})()
Event.listen('squareMeter88', function (price) { // 订阅消息
console.log('价格1:'+price)
})
Event.trigger('squareMeter88', 20000) // 价格1: 20000
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
发布者和订阅者
之间不知道对方的存在,需要通过消息代理
来通信
特性 | 观察者模式 | 发布订阅模式 |
---|---|---|
关系 | 直接联系 | 无直接关系,通过消息代理 |
耦合度 | 紧耦合 | 松耦合 |
适用情况 | 当组件之间依赖关系简单时 | 当组件之间依赖关系复杂时 |
# 代理模式
通过代理,间接地访问目标对象。
// 源对象
class Jack {
constructor (target) {
this.target = target;
}
send (target, msg) {
this.target.receive(msg)
}
}
// 目标对象
class Rose {
receive (msg) {
console.log('收到消息: ' + msg)
}
}
// 代理对象
class ProxyObj {
constructor () {
this.target = new Rose();
}
receive (msg) {
this.send(msg)
}
send (msg) {
this.target.receive(msg)
}
}
const proxyObj = new ProxyObj();
const jack = new Jack(proxyObj);
jack.send(proxyObj, 'nihao'); // 收到消息:nihao
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
ES6 Proxy、Vuex中对getters的访问
Jack:
- this.target为Proxy
- send
ProxyObj:
- this.target为Rose
- send
- receive
Rose:
- receive
# 中介者模式
现实中的中介者:博彩公司
- 如果没有博彩公司,上千万的人一起计算赔率、输赢是非常困难
- 有了博彩公司作为中介者对象,每个人只需跟博彩公司发生关联,由博彩公司来根据每个人的投注情况计算好赔率(彩民赢了,找博彩公司拿;输了就把钱交给博彩公司)
# 一个购买商品的例子:
如图,如果没有使用任何设计模式,这里应该是在select
、input
的各自onchange事件里,去获取当前用户所选的条件下
的库存情况。
如果使用了中介者模式,只需增加一个中介者对象
var goods = {
'red': 3
}
var mediator = (function() {
var colorSelect = document.getElementById('colorSelect'),
numberInput = document.getElementById('numberInput')
nextBtn = document.getElementById('nextBtn')
return {
changed (obj) {
var color = colorSelect.value,
number = numberInput.value,
stock = good[color]
if (obj === colorSelect) { // 如果选择的是颜色下拉框
// ...
} else if (obj === numberInput) { // 如果选择的是数量输入框
// ...
}
nextBtn.innerHTML = '购买'
}
}
})()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
可见,所有的对象会和中介者对象
通信。当这些对象发生改变时,通知中介者对象
,同时告诉中介者对象
自己的身份,以便中介者辨别是谁发生了改变,剩下的事情就交给了中介者来完成。
好处:降低各个对象之间的耦合度
缺点:中介者对象自身往往难以维护
# 装饰者模式
装饰者模式可以 动态扩展一个实现类 的功能。
一幅画无论是否需要画框,都可以挂在墙上。但当用画框装饰时,可以让自己具有外壳保护、精美外观,同时又不被“伤害”。实际上最终是画框挂在墙上。
用处:
- 扩展一个类的功能
- 功能的动态增加、动态撤销