单例模式、装饰器模式、策略模式、外观模式、代理模式、发布订阅模式、观察者模式、工厂模式
单例模式
是什么?
能保证全局拥有一个唯一的实例对象,用来统一全局的配置和某些通用数据。其核心思想在于,每次创建实例,返回的都是同一个实例。
实现方法:三种
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');
|