前言
本系列文章主要根據《JavaScript設計模式與開發實踐》整理而來,其中會加入了一些自己的思考。希望對大家有所幫助。
文章系列
概念
命令模式中的命令(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>
<button id="button1">點選按鈕1</button>
<script>
var button1 = document.getElementById(`button1`)
button1.onclick = function () {
console.log(`重新整理選單目錄`);
}
</script>
</body>
</html>
如果這個點選事件實現很複雜,需要多人合作完成,那我們不得不深入到button1.onclick內部去修改程式碼,違背了開放封閉原則
命令模式實現
<!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>
<button id="button1">點選按鈕1</button>
<script>
var button1 = document.getElementById(`button1`)
var setCommand = function (button, command) {
button.onclick = function () {
command.execute();
}
};
var MenuBar = {
refresh: function () {
console.log(`重新整理選單目錄`);
}
};
var RefreshMenuBarCommand = function (receiver) {
this.receiver = receiver;
};
RefreshMenuBarCommand.prototype.execute = function () {
this.receiver.refresh();
};
var refreshMenuBarCommand = new RefreshMenuBarCommand(MenuBar);
setCommand(button1, refreshMenuBarCommand);
</script>
</body>
</html>
JavaScript 作為將函式作為一等物件的語言,跟策略模式一樣,命令模式也早已融入到了 JavaScript 語言之中。
js中的命令模式
<!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>
<button id="button1">點選按鈕1</button>
<script>
var button1 = document.getElementById(`button1`)
var setCommand = function (button, fn) {
button.onclick = fn
};
var MenuBar = {
refresh: function () {
console.log(`重新整理選單目錄`);
}
};
setCommand(button1, MenuBar.refresh)
</script>
</body>
</html>
設定背景色的例子
撤銷命令
我們現在來實現一個撤銷操作的例子:
介面上有四個按鈕,三個可以設定不同的背景色,undo按鈕可以撤銷上一次的操作
<!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>
<script>
var button1 = document.getElementById(`button1`)
var button2 = document.getElementById(`button2`)
var button3 = document.getElementById(`button3`)
var undo = document.getElementById(`undo`)
var div = document.getElementById(`div`)
var cacheColor = []
var setCommand = function (button, fn) {
button.onclick = fn
};
var commond = {
cache: [],
receiver: null,
execute(newBgColor) {
this.cache.push(this.receiver.style.backgroundColor)
this.receiver.style.backgroundColor = newBgColor
},
undo() {
var oldBgColor = this.cache.pop()
this.receiver.style.backgroundColor = oldBgColor
},
setReceiver(target) {
this.receiver = target
}
}
commond.setReceiver(div)
button1.onclick = function () {
commond.execute(`red`)
}
button2.onclick = function () {
commond.execute(`black`)
}
button3.onclick = function () {
commond.execute(`yellow`)
}
undo.onclick = function () {
commond.undo()
}
</script>
</body>
</html>
重做操作
這裡我們增加一個redo按鈕,以恢復之前的操作,需要一個currentIndex來記錄當前的索引
<!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>
var button1 = document.getElementById(`button1`)
var button2 = document.getElementById(`button2`)
var button3 = document.getElementById(`button3`)
var undo = document.getElementById(`undo`)
var div = document.getElementById(`div`)
var commond = {
cache: [],
currentIndex: 0,
receiver: null,
execute(newBgColor) {
this.receiver.style.backgroundColor = newBgColor
this.currentIndex++
this.cache.push(newBgColor)
console.log(`execute:`, this.cache, this.currentIndex)
},
undo() {
if (this.currentIndex <= 0) return
var oldBgColor = this.cache[--this.currentIndex]
this.receiver.style.backgroundColor = oldBgColor
console.log(`undo:`, this.cache, this.currentIndex)
},
redo() {
if (this.currentIndex >= this.cache.length - 1) return
var preBgColor = this.cache[this.currentIndex + 1]
this.currentIndex++
this.receiver.style.backgroundColor = preBgColor
console.log(`redo:`, this.cache, this.currentIndex)
},
setReceiver(target) {
this.receiver = target
this.cache.push(this.receiver.style.backgroundColor)
console.log(`setReceiver:`, this.cache, this.currentIndex)
}
}
commond.setReceiver(div)
button1.onclick = function () {
commond.execute(`red`)
}
button2.onclick = function () {
commond.execute(`black`)
}
button3.onclick = function () {
commond.execute(`yellow`)
}
undo.onclick = function () {
commond.undo()
}
redo.onclick = function () {
commond.redo()
}
</script>
</body>
</html>