单例模式(Singleton Pattern)
保证一个类仅有一个实例,并且提供一个访问它的全局访问点。
目的:对于某些类来说,只有一个实例很重要,例如,一个系统中可以存在多个打印任务,但是只能有一个正在打印;一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
class Singleton {
constructor(config) {
this.config = config;
this.instance = null;
}
// 构造一个接口,用于实例化
static getInstance(config) {
if(!this.instance) {
this.instance = new Singleton(config);
}
return this.instance;
}
}
// 调用:
Singleton.getInstance({isSwitchOn: true})
|
应用场景:
- 某个配置会被多处使用
- 全局通知类弹窗,只会出现一个(React v16 Portal)
策略模式(Strategy Pattern)
定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换。
目的:将算法的使用与算法的实现分离开来,使得算法可以被替换和更新,并由调用方决定在何时使用哪种算法。
1
2
3
4
5
6
7
8
9
10
11
|
// 定义了某种奖金计算算法
const calculateBouns = (salary, level) => {
if(level === 'A') {
return salary * 4;
}else if(level === 'B') {
return salary * 2;
}
}
// 调用:
calculateBouns(100, 'A') // 400
calculateBouns(100, 'B') // 200
|
这种写无数种if else的方式维护性相当不好也不容易扩充和替换新的算法,考虑把每种算法封装成独立方法:
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
|
// 封装后的奖金计算策略
class strategyA {
calculate(salary) {
return salary * 4
}
}
class strategyB {
calculate(salary) {
return salary * 2
}
}
// 定义奖金类Bonus
class Bonus {
constructor() {
this.salary = null; // 原始工资
this.strategy = null;// 等级对应的策略对象
}
// 设置原始工资
setSalary(salary) {
this.salary = salary
}
// 设置对应的策略对象
setStrategy(strategy) {
this.strategy = strategy
}
// 计算奖金
getBonus() {
return this.strategy.calculate(this.salary)
}
}
// 调用:
const bonus = new Bonus();
bonus.setSalary(100);
bonus.setStrategy(new strategyA());
bonus.getBonus() // 400
bonus.setStrategy(new strategyB());
bonus.getBonus() // 200
|
应用场景:
- 消除代码中大片的条件分支语句
- 表单验证常常需要包含多种不同的匹配算法
代理模式(Proxy 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
|
// 图片预加载: 在真实图片资源尚未加载完成时,先展示loading图,图片下载完成之后再修改img的src属性
// 创建img DOM
const createImg = (function() {
const node = document.createElement("img")
document.body.appendChild(node)
return {
setSrc: function(src) {
node.src = src
}
}
})()
// 代理对象
const proxy = (function() {
const img = new Image()
img.onload = () => createImg.setSrc(img.src)
return {
setSrc: function(src) {
createImg.setSrc("./loading.svg")
img.src = src
}
}
})()
// 调用:
proxy.setSrc("/images/pic.png")
|
应用场景: 略
装饰模式(Decorator Pattern) (ES7装饰器)
将一个类的对象嵌入另一个对象中,由另一个对象来决定是否调用嵌入对象的行为以便扩展自己的行为。
目的: 动态地给一个对象增加一些额外的职责,例如增强功能。
1
2
3
4
5
6
7
|
// 创建秃头码农类
class Programmer{
// ...
}
// 可以发现并没有头发
Programmer.hair // undefined
|
我们给他增加一些发量:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// 生发函数
function breedHair(target) {
target.hair = 10
}
// 装饰器,duang!
@breedHair
class Programmer{
// ...
}
// 成了!
Programmer.hair // 10
|
应用场景:
注解
1
2
3
4
5
|
@testable
class Person {
@readonly
name() { return `${this.first} ${this.last}` }
}
|
显然,Person类是可测试的,而name方法是只读的。
React 的 connect
使用Redux 时,常常需要写:
1
2
3
4
|
class Bala extends React.Component {
// ...
}
export default connect(mapStateToProps, mapDispatchToProps)(Bala);
|
用装饰器,可以改写为:
1
2
3
4
|
@connect(mapStateToProps, mapDispatchToProps)
export default class Bala extends React.Component {
// ...
}
|
toggleLayer()方法用来向组件state添加layerIndex