有必要使用命令模式的情况:
情景1 现在要定义一个方法来给别人用,但你暂时还不知道这个方法谁来调用,别人也不知道你的方法名。因此需要将这个方法定义成一个通用的名称,如execute。
情景2 (批量)执行一些动作,并且这些动作还可能撤回、重做。
JavaScript命令模式
主要形式
const obj = { doSomething() { ... }, }; const objsCommand = function(obj) { ...这里可以定义一些指令属性,如用存放命令的commandList数组、撤销所需要的变动前信息oldProperty return { execute() { obj.doSomething(); } ...这里可以定义一些撤销undo、批量执行executeAll等方法 } },
举例
1、基本
定义了一个MenuBar.refresh()方法用以刷新菜单
const MenuBar = { refresh() { console.log('刷新界面'); }, };
再定义一个Command对象用以将上述方法封装为xxxCommand.execute()形式
const RefreshMenuBarCommand = function(receiver) { return { execute() { receiver.refresh(); }, }; };
定义一个设置命令方法,用以将按钮DOM和执行的命令逻辑关联起来
const setCommand = function(button, command) { button.onClick = function() { Command.execute(); }, };
调用示例
const refreshMenuBarCommand = RefreshMenuBarCommand(Menubar);
setCommand(button1, refreshMenuBarCommand);
2、带撤销功能的命令模式
const MoveCommand = function(receiver, position) { this.receiver = receiver; this.position = position; this.oldPosition = oldPosition; }; MoveCommand.prototype.execute = function() { this.receiver.move('right', this.position, 1000, 'ease'); // 即获取节点在起点相对于窗口的位置 this.oldPosition = this.receiver.dom.getBoundingClientRect()[this.receiver.propertyName]; }; MoveCommand.prototype.undo = function() { this.receiver.move('right', this.oldPosition, 1000, 'ease'); },
3、宏命令
即将一系列方法全部封装为Command,他们的执行方法均为execute(),如:
const closeDoorCommand = { execute() { console.log('关门'); }, }; const openPcCommand = { execute() { console.log('开电脑'); }, }; const openQQcomand = { execute() { console.log('登录QQ'); }, };
再定义一个宏命令对象,作用是将上面的命令存起来,再遍历执行
const MacroCommand = function() { // 由于需要一个类似全局对象的commandList来存命令,这里采用闭包来实现 return { commandList: [], add(command) { this.commandList.push(command); }, execute() { this.commandList.forEach(command => { command.execute(); }); }, }; };
使用示例
const macroCommand = MacroCommand();
macroCommand.add(closeDoorCommand);
macroCommand.add(openPcCommand);
macroCommand.add(openQQcomand);
macroCommand.execute();
参考文献:
[1] 《JavaScript设计模式与开发时间》,曾探,中国工信出版集团.