>[success] # 撤销 ~~~ 1.撤销:撤销操作的实现一般是给命令对象增加一个名为 unexecude 或者 undo 的方法,在该方法里执 行 execute 的反向操作。 2.书中举了一个例子,有一个操作点击后页面中的小球会移动100个像素,为了可以让小球回到初始 的位置,我们可以定义一个方法'unexecude ',这个方法会记录小球移动前的位置,这个方法我们叫它 为'撤销' ~~~ >[danger] ##### 代码 ~~~ 1.下面是书中代码,通过代码可以发现'MoveCommand' 这个指令对象不像之前我们只提供了一个'execute ' 方法,现在我们还提供了一个'undo'方法用来记录小球的初始位置 ~~~ ~~~ var MoveCommand = function( receiver, pos ){ this.receiver = receiver; this.pos = pos; this.oldPos = null; }; MoveCommand.prototype.execute = function(){ this.receiver.start( 'left', this.pos, 1000, 'strongEaseOut' ); this.oldPos = this.receiver.dom.getBoundingClientRect()[ this.receiver.propertyName ]; // 记录小球开始移动前的位置 }; MoveCommand.prototype.undo = function(){ this.receiver.start( 'left', this.oldPos, 1000, 'strongEaseOut' ); // 回到小球移动前记录的位置 }; ~~~ >[success] # 重做 ~~~ 1.书中说了一些特殊情况的例子: 1.1.我们需要撤销一系列的命令。比如在一个围棋程序中,现在已经下了 10 步棋,我们需要 一次性悔棋到第 5 步。在这之前,我们可以把所有执行过的下棋命令都储存在一个历史列表中, 然后倒序循环来依次执行这些命令的 undo 操作,直到循环执行到第 5 个命令为止 1.2.在一个Canvas 画图的程序中,画布上有一些点,我们在这些点之间画了 N 条曲线把这些 点相互连接起来,当然这是用命令模式来实现的。但是我们却很难为这里的命令对象定义一个擦 除某条曲线的undo 操作,因为在 Canvas 画图中,擦除一条线相对不容易实现。 2.在某些情况下无法顺利地利用 undo 操作让对象回到 execute 之前的状态。这时候最好的办法是先清除, 然后把刚才执行过的命令全部重新执行一遍,这一点同样 可以利用一个历史列表堆栈办到。记录命令日志, 然后重复执行它们,这是逆转不可逆命令的一 个好办法。 ~~~ >[danger] ##### 书中的案例 ~~~ 1.书中的作者做了一个案例,这个案例就是记录你按w s a d,键位,一个按钮点击完后,会重新执行你 刚才的操作,这样就更容易理解了如果遇到'Canvas '这种较为特殊的无法后退的情况,可以通过 '声明一个数组,取值的时候通过堆栈' 2.可以看出在每一次操作的时候都有都会有个一'push数组的操作,用来记录你每次的操作', 在通过'shift()'依次执行 3.通过这案例也拓展了一个思路,如何记录上一次的操作,可以利用数组但数组就不用保存每一步的操作, 个人自己思路数组长度为2固定的,初始的时候数组第一位记录初始化的效果,当数组长度超过二的时候删除 数组中的第一位,这样在push进来的新的让数组长度再次变成2 ~~~ ~~~ <html> <head> <meta charset="utf-8"> </head> <body> <button id="replay">播放录像</button> </body> <script> var Ryu = { attack: function(){ console.log( '攻击' ); }, defense: function(){ console.log( '防御' ); }, jump: function(){ console.log( '跳跃' ); }, crouch: function(){ console.log( '蹲下' ); } }; // ----------------正常思路用法------------------------ // // 需要以此类推写四个命令 // var AttackCommand = function(reciver){ // this.reciver = reciver // } // AttackCommand.prototype.execute = function(){ // this.reciver.attack() // } // // invoker 调用者 // var setCommand = function(command){ // command.execute() // } // -------------------------------------------------- // 利用策略模式配合 解决需要写四个 命令 var makeCommand = function( receiver, state ){ // 创建命令 return function(){ receiver[ state ](); } }; var commands = { "119": "jump", // W "115": "crouch", // S "97": "defense", // A "100": "attack" // D }; var commandStack = []; // 保存命令的堆栈 document.onkeypress = function( ev ){ var keyCode = ev.keyCode, command = makeCommand( Ryu, commands[ keyCode ] ); if ( command ){ command(); // 执行命令 commandStack.push( command ); // 将刚刚执行过的命令保存进数组队列中 } }; document.getElementById( 'replay' ).onclick = function(){ // 点击播放录像 var command; while( command = commandStack.shift() ){ // 从队列里依次取出命令并执行 command(); } }; </script> </html> ~~~