撤銷和回退的實現

功能點描述

你們應該都使用過瀏覽器的後退和前進功能,它就是咱們今天的主題:撤銷和回退。其實不光是在瀏覽器裏面,在衆多的工具軟件內也都有相似的功能,遠的不說,例如: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展現連接網站

若是此教程對你有幫助的話,請賞賜一個小星星 -_-!

相關文章
相關標籤/搜索