常用設(shè)計模式
命令模式
命令模式中的命令(command)指的是一個執(zhí)行某些特定事情的指令岂贩。
類似場景
有時候需要向某些對象發(fā)送請求匈辱,但是并不知道請求的接收 者是誰寺擂,也不知道被請求的操作是什么。
如快餐店點餐砾医,我們不需要知道廚師是誰拿撩,我們只需要把訂單交給服務(wù)員, 然后廚師長產(chǎn)出
優(yōu)缺點
請求發(fā)送者和請求接收者能夠消除彼此之間的耦合關(guā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="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語句,曾經(jīng)學(xué)過最簡單的策略模式雛形就是使用數(shù)組的方式解決傳入數(shù)字得到對應(yīng)星期幾問題的算法错邦。
example:比如公司的年終獎是根據(jù)員工的工資和績效來考核的探赫,績效為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
代理模式
代理模式是為一個對象提供一個代用品或占位符毛仪,以便控制對它的訪問。
場景: 比如芯勘,明星都有經(jīng)紀人作為代理箱靴。如果想請明星來辦一場商業(yè)演出,只能聯(lián)系他的經(jīng)紀人荷愕。經(jīng)紀人會把商業(yè)演出的細節(jié)和報酬都談好之后衡怀,再把合同交給明星簽棍矛。
分類
保護代理:
于控制不同權(quán)限的對象對目標對象的訪問,如上面明星經(jīng)紀人的例子
虛擬代理:
把一些開銷很大的對象抛杨,延遲到真正需要它的時候才去創(chuàng)建够委。如短時間內(nèi)發(fā)起很多個http請求,我們可以用虛擬代理實現(xiàn)一定時間內(nèi)的請求統(tǒng)一發(fā)送
Tip: 函數(shù)防抖關(guān)于防抖和節(jié)流這個寫的好
防抖(debounce)
所謂防抖怖现,就是指觸發(fā)事件后在 n 秒內(nèi)函數(shù)只能執(zhí)行一次慨绳,如果在 n 秒內(nèi)又觸發(fā)了事件,則會重新計算函數(shù)執(zhí)行時間真竖。
節(jié)流(throttle)
所謂節(jié)流脐雪,就是指連續(xù)觸發(fā)事件但是在 n 秒中只執(zhí)行一次函數(shù)。節(jié)流會稀釋函數(shù)的執(zhí)行頻率恢共。
優(yōu)缺點
- 可以保護對象
- 優(yōu)化性能战秋,減少開銷很大的對象
- 緩存結(jié)果
惰性請求
<!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'
}
// 經(jīng)紀人
let agent = new Proxy(star, {
get: function (target, key) {
if (key === 'phone') {
// 返回經(jīng)紀人自己的手機號
return '18611112222'
}
if (key === 'price') {
// 明星不報價讨韭,經(jīng)紀人報價
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)
發(fā)布訂閱模式
如果忘記了,就去看vue源碼吧透硝,沒有寫的比它更好的了~
觀察者模式
迭代器模式
內(nèi)部迭代器函數(shù)
內(nèi)部已經(jīng)定義好了迭代規(guī)則狰闪,它完全接手整個迭代過程,外部只需要一次初始調(diào)用濒生,去原型上找這個
Symbol(Symbol.iterator)
判斷當(dāng)前變量或者實例是否可以迭代
這里主要指的外部迭代器函數(shù)埋泵,自定義和封裝的
loadsh each 函數(shù)
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())
}
優(yōu)缺點
優(yōu)點: 內(nèi)部迭代器在調(diào)用的時候非常方便,外界不用關(guān)心迭代器內(nèi)部的實現(xiàn)罪治,跟迭代器的交互也僅 僅是一次初始調(diào)用
缺點: 由于內(nèi)部迭代器的迭代規(guī)則已經(jīng)被提前規(guī)定丽声,默認 forEach 函數(shù)就無法同時迭代多個數(shù)組forEach(...args,()=>{})