设计模式概念
前人总结的代码最佳实践。
设计模式是一套被反复使用、多人知晓的、经过分类的、代码设计经验的总结。
工程师基础(内力,决定走多远)
算法和数据结构、设计模式、网络协议(TCP/IP 通信 缓存)、操作系统、计算机组成、数据库(和数据结构也息息相关,红黑树)、编译原理(vue的compile模块)
订阅/发布模式(观察者模式)
class Event{ constructor(){ this.callbacks = {} } $on(name, fn){ // 监听 // if(!this.callbacks[name]){ // this.callbacks[name] = [] // } // this.callbacks[name].push(fn) // 上述代码的简写 (this.callbacks[name] || (this.callbacks[name]=[])).push(fn) } $emit(name, arg){ const cbs = this.callbacks[name] if(cbs){ cbs.forEach(c=>{ c.call(this, arg) }) } } $off(name){ this.callbacks[name] = null } } let event = new Event() event.$on('event1', arg => { console.log('event1触发', arg) }) event.$on('event1', arg => { console.log('又一个event1触发', arg) }) event.$on('event2', arg => { console.log('event2触发', arg) }) event.$emit('event1', 'myArg') event.$emit('event2', 'myArg2') event.$off('event1') event.$emit('event1', 'myArg')
单例模式
定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有一个实例对象。
适用场景:一个单一对象。比如:弹窗,无论点击多少次,弹窗只应该被创建一次,实现起来也很简单,用一个变量缓存即可
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <style> .modal{ position: fixed; border: 2px solid red; width: 300px; height: 300px; top: 30%; left: 50%; margin-left: -150px; text-align: center; padding: 20px; } </style> </head> <body> <div id="model-btn">点我</div> <script> function getSingle(fn){ // 使用闭包存储期望的单例实例 // 把函数fn变成单例 let result return function(){ return result || (result=fn.apply(this, arguments)) } } function createModalLayer(){ console.log('新建弹窗') // 期望弹窗是全局唯一的 let div = document.createElement('div') div.innerHTML = '我是一个弹窗' div.className = 'modal' div.style.display = 'none' div.addEventListener('click', function(){ div.style.display = 'none' }, false) document.body.appendChild(div) return div } // 只需要对原本的函数包一层,就变成单例了 createModalLayer = getSingle(createModalLayer) document.getElementById('model-btn').addEventListener('click', function(){ // 新建一个弹窗 const modalLayer = createModalLayer() // 修改弹窗的内容 modalLayer.style.display = 'block' modalLayer.innerHTML = '登录' + new Date() }, false) </script> </body> </html>
策略模式
// 判断绩效:绩效S为4倍工资,A为3倍工资,B为2倍工资 function calculate(level, salary){ if(level==='S'){ return salary*4 } if(level==='A'){ return salary*3 } if(level==='B'){ return salary*2 } return salary }
上述代码如果想加入新的条件,比如: SS绩效 5倍工资
就得多加一条 if 判断
if(level==='SS'){ return salary*5 }
这种我们称为面条代码,不好维护而且阅读起来比较困难
将至改造成策略模式(策略 + 使用)
// 新的策略,就扩展这个对象---这里可以使用配置 {"S":4, "A":3, "B":2} ,也可以是接口返回 const policy = { 'S': function(salary){ return salary * 4 }, 'A': function(salary){ return salary * 3 }, 'B': function(salary){ return salary * 2 } } // 使用层(业务层) function calculate(level, salary){ return policy[level] ? policy[level](salary) : salary }
代理模式
代理模式的定义:为一个对象提供一个代用品或占位符,以便控制对它的访问。
常用的虚拟代理形式:某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候采取创建(例:使用虚拟代理实现图片懒加载)
图片懒加载的方式:先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把完成的图片加载到img标签里面
原本的代码:
// 图片实例有一个setSrc方法,会先设置loading,然后再显式图片 let imgFunc = (function(){ let imgNode = document.createElement('img') document.body.appendChild(imgNode) return { setSrc: function(src){ imgNode.src = src } } })() imgFunc.setSrc('https://res.kaikeba.com/other/123/20191209112316-88923/FvFBVu8ZVu5aLU6yWTvGn8dwrjhm.png')
使用代理后
// 图片实例有一个setSrc方法,会先设置loading,然后再显式图片 let imgFunc = (function(){ let imgNode = document.createElement('img') document.body.appendChild(imgNode) return { setSrc: function(src){ // 图片较大时会比较耗时 imgNode.src = src } } })() // 使用代理模式,增加loading的功能 let proxyImage = (function(){ let img = new Image() img.onload = function(){ // onload加载完毕,再设置 imgFunc.setSrc(this.src) } return { setSrc(src){ // 先设置loading imgFunc.setSrc('./loading.jpg') img.src = src } } })() // 使用proxyImage代理操作,设置src proxyImage.setSrc('https://res.kaikeba.com/other/123/20191209112316-88923/FvFBVu8ZVu5aLU6yWTvGn8dwrjhm.png')
中介者模式
定义:通过一个中介者对象,其他所有的相关对象都通过该中介者对象来通信,而不是相互引用,当其中一个对象发生改变时,只需要通知中介者对象即可。通过中介者模式可以解除对象与对象之间的紧耦合关系。
适用的场景:例如购物车需求,存在商品选择表单、颜色选择表单、购买数量表单等待,都会触发change事件,那么可以通过中介者来转发处理这些事件,实现各个事件间的解耦,仅仅维护中介者对象即可。
redux,vuex都属于中介者模式的实际应用,我们把共享的数据,抽离成一个单独的 store,每个都通过 store 这个中介来操作对象。
装饰器模式
1