我的编程空间,编程开发者的网络收藏夹
学习永远不晚

JavaScript撤销恢复的方法是什么

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

JavaScript撤销恢复的方法是什么

今天小编给大家分享一下JavaScript撤销恢复的方法是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

一、初期设想

栈似乎很合适, 用栈存储状态.

最近的一次操作入栈存在于栈顶, 而撤销操作只需要对栈顶的状态进行操作, 这遵循栈的后进先出原则(LIFO).

然后再设置一系列方法去操作栈, 增加一点安全性.

首先是各种功能应该在什么地方发起出入栈操作, 这里有一大堆js文件.

其次对于不同的功能需要收集什么参数来组织状态入栈.

不同的功能出栈应该分别进行什么操作, 回撤出栈肯定要调这堆文件里的方法来把老状态填回去.

二、如何收集状态

先写个demo,我建了一个数组栈放在class Manager, 设置了入栈方法push, 出栈方法pop以及查看栈顶的peek方法.

class Manager {    constructor() {        this.stats= [];    }    push(action) {        this.stats.push(action);    }    pop() {        const nowAction = this.doActions.pop();    }    peek(which) {        return this.doActions[this.doCount];    }}export { Manager }

收集状态就不得不去满地的找了, 操作都写好了, 还是不要乱动.

除非单独写一套独立的逻辑, 执行系统的同时也执行我自己的, 但这基本是要重写一套系统了

1.通信尝试

但还是要想办法把各处的数据都划拉到Manager里.

呃, 老实说我并没有什么原生开发的经验, 我在多个文件里引入了Manager类并且期望着这些文件可以基于Manager建立联络实现数据共享, 比如在a.js和b.js内:

只是举个例子, 不要用一个字母去命名文件.

// a.jsimport { manager } from "./backup/manager.js";const manager = new Manager();const action = {  name: 'saveWorldList',  params: {    target: '108',    value: [      world: {        psr: {},        annotation: {}      }    ]  }}for (let i = 0; i < 3; i++) {  manager.push(action);}
// b.jsimport { manager } from "./backup/manager.js";const manager = new Manager();const undoAction = manager.pop();console.log(undoAction);

然而这样做并不能实现数据共享, 每一个刚刚实例化出来的对象都是崭新的.

const manager = new Manager();

只是使用原始干净的class Manger实例化了一个仅存在于这个模块里的对象manager.

2.如何通信

如果将一个对象放在公用的模块里, 从各个文件触发去操作这一个对象呢&hellip;公用模块里的数据总不至于对来自不同方向的访问做出不同的回应吧?

class Manager {    constructor() {        this.stats= [];    }    push(action) {        this.stats.push(action);    }    pop() {        const nowAction = this.doActions.pop();    }    peek(which) {        return this.doActions[this.doCount];    }}const manager = new Manager();export { manager }

之后分别在各个js文件引入manager, 共同操作该模块内的同一个manager, 可以构成联系, 从不同位置向manager同步数据.

manager几乎像服务器里的数据库, 接受存储从各处发送的数据.

三、管理者与执行者

现在入栈方案基本确定了, 一个入栈方法push就能通用, 那出栈怎么办呢.

不同的操作必须由不同的出栈方法执行.

最初设想是写一个大函数存在class manager里, 只要发起回撤就调这个函数, 至于具体执行什么, 根据参数去确定.

但是这方法不太好.

首先, 我会在用户触发ctrl + z键盘事件时发起回撤调用回撤函数, 但是我只在这一处调用, 如何判定给回撤函数的参数该传什么呢? 如果要传参, 我怎么在ctrl + z事件监听的地方获取到该回撤什么操作以传送正确的参数呢?

另外, 如果这样做, 我需要在manager.js这一个文件里拿到所有回撤操作需要的方法和它们的参数, 这个项目中的大部分文件都以一个巨大的类起手, 构造函数需要传参, 导出的还是这个类, 我如果直接在manager里引入这些文件去new它们, 先不说构造函数传参的问题, 生成的对象是崭新的, 会因为一些方法没有调用导致对象里的数据不存在或者错误, 而我去使用这些数据自然也导致错误.

我最好能拿到回撤那一刻的数据, 那是新鲜的数据, 是有价值的.

另外manager会在许多地方引入, 它最好不要太大太复杂.

1.数据驱动

传参的方案十分不合理, 最好能用别的方法向回撤函数描述要执行怎样的回撤操作.

在入栈的时候直接于数据中描述该份数据如何进行回撤似乎也行, 但是以字符串描述出来该如何执行?

switch吗, 那需要在回撤函数内写全部处理方案, 哪怕处理方案抽离也需要根据switch调取函数, 就像这样:

class Manager {  constructor () {    this.stats = [];  }  pop() {    const action = this.stats.pop();    switch (action) {  planA:         this.planAFun(action.params);      break;      planB:         this.planBFun(action.params);      break;      // ...    }  }}

将来万一要加别的功能的回撤, 一个函数百十行就不太好看了, 还是在类里面的函数.

那&hellip;把switch也抽出去? 似乎没必要.

2.管理者

参考steam, 嗯, 就是那个游戏平台)

steam可以看作游戏的启动器吧, 抛开人工操作, 如果需要启动游戏,那么先启动steam, steam再去启动游戏, steam可以看作一个管理者.

管理者只需要去决定, 并且调用分派事项给正确的执行者就好, 管理者自己不执行.

参考你老板.

然后Manager可以作为这样一个角色, 它只负责维护状态和分配任务:

import { Exec } from './exec.js';import { deepCopy } from "../util.js";const executors = new Exec(); // 执行者名单class Manager {  constructor() {    this.editor = null;    this.doCount = 0;    this.doActions = [];    this.undoCount = 0;    this.undoActions = [];    this.justUndo = false;    this.justRedo = false;  }  do(action) { // 增加状态    if (this.justUndo || this.justRedo) { // undo/redo后, world不应立即入栈      this.justUndo === true && (this.justUndo = false);      this.justRedo === true && (this.justRedo = false);      return;    }    this.previousWorld = action.params.value;    this.doActions.push(action);    this.doCount++    console.log("Do: under control: ", this.doActions);  }  undo() { // 回撤事项分配    if (this.doActions.length === 1) {      console.log(`Cannot undo: doSatck length: ${this.doActions.length}.`);      return;    }    const nowAction = this.doActions.pop();    this.doCount--;    this.undoActions.push(nowAction);    this.undoCount++;    const previousAction = this.peek('do');    const executor = this.getFunction(`${previousAction.name}Undo`);    executor(this.editor, previousAction.params)    this.justUndo = true;    console.log(`Undo: Stack now: `, this.doActions);  }  redo() { // 恢复事项分配     if (this.undoActions.length === 0) {       console.log(`Connot redo: redoStack length: ${this.undoActions.length}.`);       return;     }    const nowAction = this.undoActions.pop();    this.undoCount--;    this.doActions.push(nowAction);    this.doCount++;    const previousAction = nowAction;    const executor = this.getFunction(`${previousAction.name}Redo`);    executor(this.editor, previousAction.params);    this.justRedo = true;    console.log(`Redo: Stack now: `, this.doActions);  }  getFunction(name) {    return executors[name];  }  reset() { // 重置状态    this.doCount = 0;    this.doActions = [];    this.undoCount = 0;    this.undoActions = []  }  peek(which) { // 检视状态    if (which === 'do') {      return this.doActions[this.doCount];    } else if (which === 'undo') {      return this.undoAction[this.undoCount];    }  }  initEditor(editor) {    this.data = editor;  }}const manager = new Manager();export { manager }

justUndo/justRedo, 我的状态收集是在一次请求前, 这个请求函数固定在每次世界变化之后触发, 将当前的世界状态上传. 所以为了避免回撤或恢复世界操作调用请求函数将回撤或恢复的世界再次重复加入栈内而设置.

undo或者redo这两种事情发生后, 执行者manager通过原生数组方法获取到本次事项的状态对象(出栈), 借助getFunction(看作它的秘书吧)访问执行者名单, 帮自己选取该事项合适的执行者, 并调用该执行者执行任务(参考undo, redo函数体).

执行者名单背后是一个函数库一样的结构, 类似各个部门.

这样只需要无脑undo()就好, manager会根据本次的状态对象分配执行者处理.

do这个操作比较简单也没有多种情况, 就没必要分配执行者了&hellip;

3.执行者

执行者名单需要为一个对象, 这样getFunction()秘书才能够为manager选出合适的执行者, 执行者名单应为如下结构:

// 执行者有擅长回撤(undo)和恢复(redo)的两种{  planA: planAFun (data, params) {    // ...  },  planAUndo: planAUndoFun (data, params) {    // ...  },  planB: planBFun () {    // ...  },  planBUndo: planBUndoFun (data, params) {    // ...  }  ...}

也好, 那就直接把所有执行者抽离为一个类, 实例化该类后自然能形成这种数据结构:

class Exec { // executor  saveWorldRedo (data, params) {    // ...  }  saveWorldUndo (data, params) {    // ...  }  initialWorldUndo (data, params) {    // ...  }}export { Exec };

实例化后:

{  saveWorldRedo: function (data, params) {    // ...  },  saveWorldUndo: function (data, params) {    // ...  },  initialWorldUndo: function (data, params) {    // ...  }}

正是需要的结构.

getFunction可以由解析状态对象进而决定枚举executor对象中的哪个执行者出来调用:

const executor = getFunction (name) {  return executors[name];}

以上就是“JavaScript撤销恢复的方法是什么”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

JavaScript撤销恢复的方法是什么

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

JavaScript撤销恢复的方法是什么

今天小编给大家分享一下JavaScript撤销恢复的方法是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、初期设想栈似
2023-07-05

JavaScript撤销恢复操作的实现方法详解

这篇文章主要介绍了JavaScript撤销恢复操作的实现方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
2023-02-22

windows撤销与恢复快捷键是什么

这篇文章主要讲解了“windows撤销与恢复快捷键是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“windows撤销与恢复快捷键是什么”吧!撤销与恢复快捷键:答:撤销快捷键是:ctrl+
2023-07-02

git撤销本地commit的方法是什么

撤销本地的 commit 可以通过以下两种方式实现:1. 使用 git reset 命令:可以使用 git reset 命令回退到之前的 commit,撤销最新的 commit 并将代码回退到指定的 commit。```git reset
2023-08-23

raid数据恢复的方法是什么

这篇文章主要介绍了raid数据恢复的方法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇raid数据恢复的方法是什么文章都会有所收获,下面我们一起来看看吧。【物理服务器与逻辑存储介绍】 客户使用一台IBM
2023-06-04

mongodb备份和恢复的方法是什么

在MongoDB中,可以使用mongodump和mongorestore命令来备份和恢复数据库。备份数据:使用mongodump命令来备份整个数据库或指定的集合。示例:mongodump --db --ou
mongodb备份和恢复的方法是什么
2024-04-09

Oracle rman异机恢复的方法是什么

Oracle RMAN(Recovery Manager)是Oracle数据库的备份和恢复工具。RMAN提供了异机恢复的方法,可以将数据库备份文件从一个主机恢复到另一个主机。下面是Oracle RMAN异机恢复的方法:在目标主机上安装和配
Oracle rman异机恢复的方法是什么
2024-04-09

mysql恢复数据库的方法是什么

MySQL恢复数据库的方法主要有以下几种:1. 使用备份文件恢复:如果有事先备份的数据库文件,可以使用MySQL提供的命令行工具(如mysql命令)或可视化工具(如phpMyAdmin)进行导入,将备份文件中的数据重新导入到数据库中。2.
2023-10-09

sqlserver数据库恢复的方法是什么

SQL Server数据库的恢复方法通常有以下几种:使用备份文件恢复:可以利用SQL Server Management Studio (SSMS)或T-SQL命令来还原数据库备份文件来恢复数据库。使用事务日志恢复:如果数据库发生了意外故障
sqlserver数据库恢复的方法是什么
2024-04-09

windows easyrecovery恢复文件的方法是什么

本篇内容主要讲解“windows easyrecovery恢复文件的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“windows easyrecovery恢复文件的方法是什么”吧!ea
2023-07-01

win11备份与恢复的方法是什么

Windows 11 提供了多种备份和恢复方法,以下是几种常用的方法:1. 使用 Windows 備份與還原工具:Windows 11 附带了备份和还原工具(Backup and Restore),可以创建系统映像和文件备份。你可以在 "设
2023-09-02

mysql数据库恢复的方法是什么

MySQL数据库恢复的方法有以下几种:通过备份文件恢复:如果有定期备份数据库的文件,可以使用备份文件进行恢复。首先,停止MySQL服务,然后将备份文件拷贝到MySQL数据目录下,最后启动MySQL服务即可完成恢复。利用二进制日志进行恢复:M
mysql数据库恢复的方法是什么
2024-04-09

sqlserver恢复表数据的方法是什么

在SQL Server中,恢复表数据的方法通常包括以下几种:使用事务日志备份进行恢复:如果表数据被意外删除或修改,可以通过还原事务日志备份来恢复表数据到之前的状态。使用备份进行恢复:如果有定期备份数据库的策略,可以通过还原数据库备份来恢复表
sqlserver恢复表数据的方法是什么
2024-04-09

ubuntu恢复默认源的方法是什么

要恢复Ubuntu的默认软件源,可以通过以下步骤进行操作:1. 打开终端(Ctrl+Alt+T)。2. 编辑 `/etc/apt/sources.list` 文件。可以使用命令 `sudo nano /etc/apt/sources.lis
2023-10-12

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录