常用设计模式
命令模式
命令模式中的命令(command)指的是一个执行某些特定事情的指令。
类似场景
有时候需要向某些对象发送请求,但是并不知道请求的接收 者是谁,也不知道被请求的操作是什么。
如快餐店点餐,我们不需要知道厨师是谁,我们只需要把订单交给服务员, 然后厨师长产出
优缺点
请求发送者和请求接收者能够消除彼此之间的耦合关系
小例子:
<!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>
</head>
<body>
<div id="div" style="height: 100px;width: 100px;background-color: blue"></div>
<button id="button1">red</button>
<button id="button2">black</button>
<button id="button3">yellow</button>
<button id="undo">undo</button>
<button id="redo">redo</button>
<script>
const button1 = document.getElementById('button1')
const button2 = document.getElementById('button2')
const button3 = document.getElementById('button3')
const undo = document.getElementById('undo')
const div = document.getElementById('div')
class Command {
constructor() {
this.cache = []
this.currentIndex = 0
this.receiver = null
}
execute(cmd, name = 'backgroundColor') {
this.receiver.style[name] = cmd
this.currentIndex++
this.cache.push(cmd)
console.log(this.cache)
// console.log('execute:', this.cache, this.currentIndex)
}
undo(name = 'backgroundColor') {
if(this.currentIndex <= 0) return
let oldCmd = this.cache[--this.currentIndex]
this.receiver.style[name] = oldCmd
console.log('undo:', this.cache, this.currentIndex)
}
redo(name = 'backgroundColor') {
if (this.currentIndex >= this.cache.length - 1) return
let preColor = this.cache[this.currentIndex + 1]
this.currentIndex++
this.receiver.style[name] = preColor
console.log('redo:', this.cache, this.currentIndex)
}
setReceiver(target, name = 'backgroundColor') {
this.receiver = target
this.cache.push(this.receiver.style[name])
console.log('setReceiver:', this.cache, this.currentIndex)
}
}
const command = new Command()
command.setReceiver(div)
button1.onclick = function () {
command.execute('red')
}
button2.onclick = function () {
command.execute('black')
}
button3.onclick = function () {
command.execute('yellow')
}
undo.onclick = function () {
command.undo()
}
redo.onclick = function () {
command.redo()
}
</script>
</body>
</html>
单例模式
只允许存在一个实例的模式
const Instance = (function(){
const obj;
return function(){
if(obj === undefined) {
obj = new Date();
}
return obj;
}
})();
const i = Instance();
策略模式
定义:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换,从而避免很多if语句,曾经学过最简单的策略模式雏形就是使用数组的方式解决传入数字得到对应星期几问题的算法。
example:比如公司的年终奖是根据员工的工资和绩效来考核的,绩效为A的人,年终奖为工资的4倍,绩效为B的人,年终奖为工资的3倍,绩效为C的人,年终奖为工资的2倍
const obj = {
"A": function(salary: number) {
return salary * 4;
},
"B" : function(salary: number) {
return salary * 3;
},
"C" : function(salary: number) {
return salary * 2;
}
};
const calculate = function(level: string, salary: number) {
return obj[level](salary);
};
console.log(calculate('A',10000)); // 40000
代理模式
代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。
场景: 比如,明星都有经纪人作为代理。如果想请明星来办一场商业演出,只能联系他的经纪人。经纪人会把商业演出的细节和报酬都谈好之后,再把合同交给明星签。
分类
保护代理:
于控制不同权限的对象对目标对象的访问,如上面明星经纪人的例子
虚拟代理:
把一些开销很大的对象,延迟到真正需要它的时候才去创建。如短时间内发起很多个http请求,我们可以用虚拟代理实现一定时间内的请求统一发送
Tip: 函数防抖关于防抖和节流这个写的好
防抖(debounce)
所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
节流(throttle)
所谓节流,就是指连续触发事件但是在 n 秒中只执行一次函数。节流会稀释函数的执行频率。
优缺点
- 可以保护对象
- 优化性能,减少开销很大的对象
- 缓存结果
惰性请求
<!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>
</head>
<body>
<div id="wrapper">
<button id="1">1</button>
<button id="2">2</button>
<button id="3">3</button>
<button id="4">4</button>
<button id="5">5</button>
<button id="6">6</button>
<button id="7">7</button>
<button id="8">8</button>
</div>
</body>
<script type="text/javascript" >
// 模拟http请求
const synchronousFile = function (id) {
console.log('开始同步文件,id 为: ' + id);
};
const inputs = document.getElementsByTagName('input')
const wrapper = document.getElementById('wrapper')
wrapper.onclick = function (e) {
if (e.target.localName === 'button') {
// synchronousFile(e.target.id)
proxySynchronousFile(e.target.id)
}
}
const proxySynchronousFile = (function () {
let cacheIds = [],
timeId = 0
return function (id) {
if (cacheIds.indexOf(id) < 0) {
cacheIds.push(id)
}
clearTimeout(timeId)
timeId = setTimeout(() => {
synchronousFile(cacheIds.join(','))
cacheIds = []
}, 1000)
}
})()
</script>
</html>
明星报价场景
// 明星
let star = {
name: 'cxk',
age: 25,
phone: '0000000000'
}
// 经纪人
let agent = new Proxy(star, {
get: function (target, key) {
if (key === 'phone') {
// 返回经纪人自己的手机号
return '18611112222'
}
if (key === 'price') {
// 明星不报价,经纪人报价
return 120000
}
return target[key]
},
set: function (target, key, val) {
if (key === 'customPrice') {
if (val < 100000) {
// 最低 10w
throw new Error('价格太低')
} else {
target[key] = val
return true
}
}
}
})
// 主办方
console.log(agent.name)
console.log(agent.age)
console.log(agent.phone)
console.log(agent.price)
// 想自己提供报价(砍价,或者高价争抢)
agent.customPrice = 150000
// agent.customPrice = 90000 // 报错:价格太低
console.log('customPrice', agent.customPrice)
发布订阅模式
如果忘记了,就去看vue源码吧,没有写的比它更好的了~
观察者模式
迭代器模式
内部迭代器函数
内部已经定义好了迭代规则,它完全接手整个迭代过程,外部只需要一次初始调用,去原型上找这个
Symbol(Symbol.iterator)
判断当前变量或者实例是否可以迭代
这里主要指的外部迭代器函数,自定义和封装的
loadsh each 函数
class Iterator {
this.list: Array<any>
this.index: number
constructor(conatiner: Container) {
this.list = conatiner.list
this.index = 0
}
next(): any {
if (this.hasNext()) {
return this.list[this.index++]
}
return null
}
hasNext(): boolean {
if (this.index >= this.list.length) {
return false
}
return true
}
}
class Container {
this.list: Array<any>
constructor(list: Array<any>) {
this.list = list
}
getIterator(): Iterator {
return new Iterator(this)
}
}
≠≠≠
// test
let container = new Container([1, 2, 3, 4, 5])
let iterator = container.getIterator()
while(iterator.hasNext()) {
console.log(iterator.next())
}
优缺点
优点: 内部迭代器在调用的时候非常方便,外界不用关心迭代器内部的实现,跟迭代器的交互也仅 仅是一次初始调用
缺点: 由于内部迭代器的迭代规则已经被提前规定,默认 forEach 函数就无法同时迭代多个数组forEach(...args,()=>{})