你們應該都使用過瀏覽器的後退和前進功能,它就是咱們今天的主題:撤銷和回退。其實不光是在瀏覽器裏面,在衆多的工具軟件內也都有相似的功能,遠的不說,例如:vscode、ppt。git
在說實現思路以前,我先上一張圖:github
眼尖的同窗可能已經看到了 Stack ,是的,你想的沒錯,其實撤銷和回退功能就是對 Stack 的使用場景之一。 首先咱們須要兩個 Stack(可撤銷棧和可回退棧)爲了更直觀的展現,我用點擊小球代替了對網站的訪問。算法
A 爲紅色小球;B 爲綠色小球;C 爲藍色小球瀏覽器
爲何叫它是特殊的棧呢,由於傳統的棧是沒有 #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;
}
}
}
複製代碼
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展現連接網站
若是此教程對你有幫助的話,請賞賜一個小星星 -_-!