什么是设计模式
- 假设有一个空房间,我们要日复一日地往里 面放一些东西。最简单的办法当然是把这些东西 直接扔进去,但是时间久了,就会发现很难从这 个房子里找到自己想要的东西,要调整某几样东 西的位置也不容易。所以在房间里做一些柜子也 许是个更好的选择,虽然柜子会增加我们的成 本,但它可以在维护阶段为我们带来好处。使用 这些柜子存放东西的规则,或许就是一种模式
目的
- 学习设计模式, 有助于我们写出可复用和可维护性高的代码
- 设计模式的原则是 找出程序中变化的地方 并将变化的封装起来
常用的设计模式
1.单例模式 比如 vuex
- 只能创建单个对象,和工厂模式相对
- 场景:
- vuex
- 实现:
class Singleton {
constructor (name) {
this.name = name
this.instance = null
}
getName () {
alert (this.name)
}
static getInstance (name) {
if (!this.instance) {
this.instance = new Singleton(name)
}
return this.instance
}
}
const ins = new Singleton('123')
const instance0 = Singleton.getIntance('000)
const instance1 = Singleton.getIntance('123')
2. 策略模式:
- 提前将可能发生的情况都考虑到位,并给出对应的解决方案
- 优点:
- 可以避免 if else 语句 代码结构清晰 更加直观 有利于维护
- 缺点:
- 往往策略集会比较多, 我们需要事先了解定义好所有的情况 才能选择一个最优的方法(不算缺点 但是违反了 最少知识原则)
- 场景:
- 轮播图中左右按钮的区分
- 左右移动方向的确定
- 小标被点击时的方向
const strategies = {
aaa ( salary ) {
return salary * 4;
},
bbb ( salary ) {
return salary * 3;
},
ccc ( salary ) {
return salary * 2;
}
};
3. 代理模式:
- 为对象提供一个代用品或者占位符, 以便控制对它的访问
- 当客户不方便直接访问一个 对象或者不满足需要的时候,提供一个替身对象 来控制对这个对象的访问,客户实际上访问的是 替身对象。替身对象对请求做出一些处理之后, 再把请求转交给本体对象
- 场景:
- 事件委托
- 服务器代理
// 主体 发送消息
function sendMsg (msg) {
console.log(msg)
}
// 代理, 对消息进行过滤
function proxySendMsg (msg) {
if (typeof msg === 'undefined') {
console.log('deny')
return
}
// 有消息则进行过滤
mag = ('' + msg).replace(/泥\s*煤/g, '')
sendMsg(msg)
}
sendMsg('泥煤呀泥 煤呀'); // 泥煤呀泥 煤呀
proxySendMsg('泥煤呀泥 煤'); // 呀
proxySendMsg(); // deny
在访问主体之前进行控制, 没有消息的时候直接在代理中返回, 拒绝访问主体, 这就是 数据保护代理的模式
有消息的时候对敏感字符进行了处理, 这就是 虚拟代理的模式
- 虚拟代理的实际应用
- 在滚动事件触发的时候,也许不需要频繁触发,我们可以引入函数节流,这是一种虚拟代理的实现
// 先定义一个防抖函数
const debounce = (fn, delay = 200) => {
let timer = null
return function () {
let arg = arguments
// 每次操作前 清除上次的定时器
clearTimeout(timer)
timer = null
// 定义新的定时器, 过一段时间后操作
timer = setTimeout(() => {
fn(arg) // 这里不使用箭头函数就要处理 this 指向 fn.apply(this,arg)
}, delay)
}
}
let count = 0
// 主体
function scrollHandle (e) {
console.log(e.type, ++count)
}
// proxy
var proxyScrollHandle = (function() {
return debounce(scrollHandle, 500)
})()
window.onscroll = proxyScrollHandle
4. 工厂模式:
- 工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型。
- 简单的说: 假如我们想在网页面里插入一些元素,而这些元素类型不固定,可能是图片、链接、文本,根据工厂模式的定义,在工厂模式下,工厂函数只需接受我们要创建的元素的类型,其他的工厂函数帮我们处理。
// 文本工厂
class Text {
constructor (text) {
this.text = text
}
insert (where) {
const txt = docoument.createTextNode(this.text)
where.appendChild(txt)
}
}
// 链接工厂
class Link {
constructor (url) {
this.url = url
}
insert (where) {
const link = documnet.createElement('a')
link.href = this.url
link.appendChild(document.createTextNode(this.url))
where.appendChild(link)
}
}