撤销和回退的实现

功能点描述

你们应该都使用过浏览器的后退和前进功能,它就是咱们今天的主题:撤销和回退。其实不光是在浏览器里面,在众多的工具软件内也都有相似的功能,远的不说,例如:vscode、ppt。git

实现思路

在说实现思路以前,我先上一张图:github

眼尖的同窗可能已经看到了 Stack ,是的,你想的没错,其实撤销和回退功能就是对 Stack 的使用场景之一。 首先咱们须要两个 Stack(可撤销栈和可回退栈)

为了更直观的展现,我用点击小球代替了对网站的访问。算法

A 为红色小球;B 为绿色小球;C 为蓝色小球浏览器

  1. 当点击 A 以后再点击 B,把 A 和 B 添加到‘可撤销栈’(等同于先访问了 A 网站,后又访问了 B 网站),当前展现的为 B

  1. 当执行撤销功能时让‘可撤销栈’出栈而且把出栈的 B 添加到‘可回退栈’内,当前展现的为 A

3. 当执行回退功能时让‘可回退栈’出栈而且把出栈的 B 添加到‘可撤销栈’内,当前展现的为 B

4. 当咱们访问了 A 和 B 以后,这时候撤销了 B ,回到了 A ,而且又点击了 C ,这时候咱们须要清空‘可回退栈’,而且把 C 添加到‘可撤销栈’

code

先实现一个特殊的栈

为何叫它是特殊的栈呢,由于传统的栈是没有 #shift() 功能的。那这里为何须要有这个函数呢,请带着问题继续往下看。bash

这里偷懒直接使用了 Array 的内置函数,传统上来说的话不该该使用 - -#数据结构

function createStack() {
                var list = [];
                return {
                    /** * 入栈 * @param {*} data */
                    push(data) {
                        list.push(data);
                    },

                    /** * 出栈 */
                    pop() {
                        return list.pop();
                    },

                    size() {
                        return list.length;
                    },

                    empty() {
                        return list.length === 0;
                    },

                    clear() {
                        list = [];
                    },
                    //删除头结点(栈底)
                    shift() {
                        list.shift();
                    },

                    peek() {
                        return list[list.length - 1];
                    },

                    getList() {
                        return list;
                    }
                }
            }
复制代码

record对象

function createRecord() {
                let undoStack = createStack();
                let rollbackStack = createStack();
                const MAX_LIMIT = 6;//最大限制点
                return {
                    //获取可撤销栈 栈顶的数据
                    //用于展现
                    getTopValue() {
                        return undoStack.peek();
                    },
                    //添加记录
                    //把数据直接添加到可撤销栈内
                    //而且清空可回退栈
                    addRecord(data) {
                        //当可撤销栈的大小大于最大的限制的话
                        //那么须要删除头结点
                        if (undoStack.size() >= MAX_LIMIT) {
                            undoStack.shift();
                        }
                        undoStack.push(data);
                        rollbackStack.clear();
                    },
                    //撤销
                    //检测可撤销栈是否为空,为空的话什么也不作
                    //否则把可撤销栈出栈的数据添加到可回退栈内
                    undoRecord() {
                        if (undoStack.empty()) return;
                        const data = undoStack.pop();
                        rollbackStack.push(data);
                    },
                    //回退
                    //检测可回退栈是否为空,为空的话什么也不作
                    //把可回退栈出栈的数据添加到可撤销栈内
                    rollbackRecord() {
                        if (rollbackStack.empty()) return;
                        const data = rollbackStack.pop();
                        undoStack.push(data);
                    },
                    getUndoStack() {
                        return undoStack.getList();
                    },
                    getrollbackStack() {
                        return rollbackStack.getList();
                    }
                }
            }
复制代码

咱们在使用 vscode 写代码的时候有没有发现撤销到必定的数量时,就撤销不回去了。是的,这个逻辑就是在 #addRecord() 内处理的。当‘可撤销栈’的大小大于限制数时,那么须要抛弃掉最初的数据,也就是删除头结点。这也是说明为何咱们实现的 Stack 内有 #shift() 功能。数据结构和算法

总结

以上的逻辑我也是在公司项目逻辑中抽离出来的,由于接手的好几个项目都要实现此功能。固然了公司项目中我又加了 Command 模式来搭配使用,若是你们有兴趣的话,后续我能够在更新如何搭配 Command 模式使用。最后想说的是,数据结构和算法谁学谁知道,真香。函数

连接

代码连接工具

Demo展现连接网站

若是此教程对你有帮助的话,请赏赐一个小星星 -_-!

相关文章
相关标签/搜索