单例模式、装饰器模式、策略模式、外观模式、代理模式、发布订阅模式、观察者模式、工厂模式

单例模式

是什么?

​ 能保证全局拥有一个唯一的实例对象,用来统一全局的配置和某些通用数据。其核心思想在于,每次创建实例,返回的都是同一个实例。

实现方法:三种

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
// 饿汉模式:一开始就创建一个实例,之后全部返回这个实例
function One(){
if(!One.instance){
this.fn = ()=>{
console.log('>>>>>');
}
this.balabala = 'QAQ';
One.instance = this;
}
return One.instance;
}

// 懒汉模式:调用时才创建一个实例
function One(){
if(One.instance)
return One.instance;
else{
this.fn = ()=>{
console.log('>>>>>');
}
this.balabala = 'QAQ';
One.instance = this;
}
}

// 类静态方法创建
class One{
instance = null;
constructor(){
this.balabala = 'QAQ';
this.fn = ()=>{
console.log('>>>>>');
}
}
static getInstance = ()=>{
if(!this.instance)
this.instance = new One();
return this.instance;
}
}

优点:

  • 单例模式的所有实例化都是同一个实例,确保所有的对象访问的是一个。
  • 具有一定的伸缩性,由类自身控制实例化过程。
  • 内存只存在一个对象,节约资源,在频繁的创建和销毁时可以提高系统性能。
  • 避免共享资源对内存的多重占用。

缺点:

  • 滥用实例可能会导致单例类过于臃肿,被回收时可能会导致某些状态丢失。
  • 单例类违背了“单一职责”原则。他的职责过重,这其实是一种设计弊端。
  • 多线程问题。在使用Web Worker或者node的多线程时可能会导致多个实例被创建。

场景

​ 实现全局loading遮罩层,如果用户连续调用loading,直接返回第一个loading的实例即可,这样就不会出现重复的loading。ElementUI中的loading采用的便是单例模式。

外观模式

是什么?

​ 为系统中的一组接口提供一个统一的高层接口,使得系统更容易使用。

场景

​ 兼容浏览器事件绑定(事件监听器)

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
const EventUtils = {
addEvent: function(element, type, handler){
if(element.addEventListener)
element.addEventListener(type, handler, false);
else if(element.attachEvent)
element.attachEvent('on'+type, handler);
else
element['on'+type]=handler;
},
removeEvent: function(element, type, handler){
if(element.removeEventListener)
element.removeEventListener(type, handler, false);
else if(element.detachEvent)
element.detachEvent('on'+type, handler);
else
element['on'+type]=null;
},
stopProgapation: function(ev){
if(ev.stopPropagation)
ev.stopPropagation();
else
ev.cancleBubble = true;
},
preventDefault: function(ev){
if(ev.preventDefault)
ev.preventDefault();
else
ev.returnValue = false;
},
getTarget: function(ev){
return ev.target || ev.srcElement;
}
}

优点

​ 减少系统相互依赖,提高灵活性,提供安全性

缺点

​ 不符合开闭原则,修改复杂,继承重写都不适用

代理模式

是什么?

​ 为调用对象提供一个代理对象,以便对它进行控制。

场景

​ 事件委托

1
2
3
4
let ul = document.getElementById('#ul');
ul.addEventListener('click', event=>{
console.log(event.target);
})

优点

​ 将代理对象与被调用对象分离,降低系统耦合度。

缺点

​ 非直接访问存在开销

策略模式

是什么?

​ 对象的某个行为,在不同场景下,有不同的实现算法,将这些实现算法封装起来,使他们可以相互替换。

场景

​ 表单验证

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
67
68
69
70
71
72
73
74
75
const strategies = {
isNoEmpty: function(value, errorMsg){
if(value==='')
return errorMsg;
},
isNoSpace: function(value, errorMsg){
if(value.trim()==='')
return errorMsg;
},
minLength: function(value, length, errorMsg){
if(value.trim().length<length)
return errorMsg;
},
maxLength: function(value, length, errorMsg){
if(value.trim().length>length)
return errorMsg;
},
isMobile: function(value, errorMsg){
if(!/^(13[0-9]|14[5|7]|15[0|1|2|3|5|6|7|8|9]|17[7]|18[0|1|2|3|5|6|7|8|9])\d{8}$/.test(value))
return errorMsg;
}
}
class Validator{
constructor(){
this.cache = [];
}
add(dom, rules){
for(let i=0, rule;rule=rules[i++];){
let strategyAry = rule.strategy.split(':');
let errorMsg = rule.errorMsg;
this.cache.push(()=>{
let strategy = strategyAry.shift();
strategyAry.unshift(dom.value);
strategyAry.push(errorMsg);
return strategies[strategy].apply(dom, strategyAry);
})
}
}
start(){
for(let i=0, validatorFunc;validatorFunc=this.cache[i++];){
let errorMsg = validatorFunc();
if(errorMsg)
return errorMsg;
}
}
}
let registerForm = document.getElementById('registerForm');
let validateFunc = function(){
let validator = new Validator();
validator.add(registerForm.username, [{
strategy: 'isNoEmpty',
errorMsg: 'username cannot null'
},{
strategy: 'isNoSpace',
errorMsg: 'input cannot space'
},{
strategy: 'minLength:2',
errorMsg: 'minlength 2'
},{
strategy: 'maxlength:4',
errorMsg: 'maxlength 4'
}]);
validator.add(registerForm.phoneNumber, [{
strategy: 'isMobile',
errorMsg: 'input correct num'
}]);
return validator.start();
}
registerForm.onsubmit = function(){
let errorMsg = validateFunc();
if(errorMsg){
alert(errorMsg);
return false;
}
}

优点

​ 避免多重条件选择语句;提供了开放封闭原则;

缺点

​ 会在程序中增加许多策略类或策略对象

发布订阅者模式

是什么?

​ 发布者将要发布的消息交由发布订阅中心管理,订阅者根据自身情况,按需订阅中心中的消息。

场景

​ eventEmit

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
class EventEmitter{
constructor(){
this.events = new Map();
}
on(type, callback){
if(!this.events.has(type))
this.events[type] = [callback];
else
this.events[type].push(callback);
}
off(type, callback){
if(!this.events[type])
return;
this.events[type] = this.events[type].filter(item=>{
return item!==callback;
})
}
once(type, callback){
function fn(){
callback();
this.off(type, fn);
}
this.on(type, fn);
}
emit(type, ...rest){
this.events[type]&&this.events[type].forEach(fn=>fn.apply(this, rest));
}
}

观察者模式

是什么?

​ 观察者观察被观察对象,被观察对象在某个时机通知观察者,观察者就能收到来自被观察者的消息。

发布订阅者模式与观察者模式的区别

  • 涉及的对象不同
区别

装饰器模式

是什么?

​ 允许向现有的对象添加新的功能,同时不改变其结构。继承也可实现同样的功能,但是继承会为类引入静态特征,并且随着扩展功能的增多,子类会膨胀。

场景

​ 高阶组件

工厂模式

是什么?

​ 用来创建对象而不暴露创建对象的具体逻辑。将逻辑封装在一个函数中,这个函数就可以被视为一个工厂。

简单工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function Factory(career){
function User(career, work){
this.career = career;
this.work = work;
}
let work;
switch(career){
case 'coder':
work = ['code', 'fix bug'];
return new User(career, work);
break;
case 'hr':
work = ['hire', 'fire'];
return new User(career, work);
break;
}
}
let coder = new Factory('coder');
let boss = new Factory('hr');