iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >JavaScript撤销恢复的方法是什么
  • 919
分享到

JavaScript撤销恢复的方法是什么

2023-07-05 05:07:40 919人浏览 独家记忆
摘要

今天小编给大家分享一下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撤销恢复的方法是什么”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网精选频道。

--结束END--

本文标题: JavaScript撤销恢复的方法是什么

本文链接: https://www.lsjlt.com/news/349684.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • JavaScript撤销恢复的方法是什么
    今天小编给大家分享一下JavaScript撤销恢复的方法是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、初期设想栈似...
    99+
    2023-07-05
  • JavaScript撤销恢复操作的实现方法详解
    目录前言一、初期设想二、如何收集状态1.通信尝试2.如何通信三、管理者与执行者1.数据驱动2.管理者3.执行者前言 这是一个基于原生JavaScript+Three.js的系统, 我...
    99+
    2023-02-22
    JS撤销恢复操作 JS撤销操作 JS恢复操作
  • windows撤销与恢复快捷键是什么
    这篇文章主要讲解了“windows撤销与恢复快捷键是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“windows撤销与恢复快捷键是什么”吧!撤销与恢复快捷键:答:撤销快捷键是:ctrl+...
    99+
    2023-07-02
  • git撤销本地commit的方法是什么
    撤销本地的 commit 可以通过以下两种方式实现:1. 使用 git reset 命令:可以使用 git reset 命令回退到之...
    99+
    2023-08-23
    git
  • word撤销快捷键是什么
    这篇“word撤销快捷键是什么”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“word撤销快捷键是什么”文章吧。word撤销快...
    99+
    2023-07-01
  • Oracle恢复和介质恢复的方法是什么
    这篇文章主要介绍“Oracle恢复和介质恢复的方法是什么”,在日常操作中,相信很多人在Oracle恢复和介质恢复的方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Or...
    99+
    2024-04-02
  • RAC11G恢复OCR的方法是什么
    本篇内容主要讲解“RAC11G恢复OCR的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“RAC11G恢复OCR的方法是什么”吧! ...
    99+
    2024-04-02
  • MySQL数据恢复的方法是什么
    这篇文章主要讲解了“MySQL数据恢复的方法是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“MySQL数据恢复的方法是什么”吧!1 前言数据恢复的前提的...
    99+
    2024-04-02
  • ocr的备份恢复方法是什么
    这篇文章主要介绍“ocr的备份恢复方法是什么”,在日常操作中,相信很多人在ocr的备份恢复方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”ocr的备份恢复方法是什么”...
    99+
    2024-04-02
  • AMDU数据恢复的方法是什么
    这篇文章主要介绍“AMDU数据恢复的方法是什么”,在日常操作中,相信很多人在AMDU数据恢复的方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”AMDU数据恢复的方法是...
    99+
    2024-04-02
  • raid数据恢复的方法是什么
    这篇文章主要介绍了raid数据恢复的方法是什么的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇raid数据恢复的方法是什么文章都会有所收获,下面我们一起来看看吧。【物理服务器与逻辑存储介绍】 客户使用一...
    99+
    2023-06-04
  • windows easyrecovery恢复文件的方法是什么
    本篇内容主要讲解“windows easyrecovery恢复文件的方法是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“windows easyrecovery恢复文件的方法是什么”吧!ea...
    99+
    2023-07-01
  • mysql恢复数据库的方法是什么
    MySQL恢复数据库的方法主要有以下几种:1. 使用备份文件恢复:如果有事先备份的数据库文件,可以使用MySQL提供的命令行工具(如...
    99+
    2023-10-09
    mysql 数据库
  • sqlserver恢复表数据的方法是什么
    在SQL Server中,恢复表数据的方法通常包括以下几种: 使用事务日志备份进行恢复:如果表数据被意外删除或修改,可以通过还原...
    99+
    2024-04-09
    sqlserver
  • mongodb备份和恢复的方法是什么
    在MongoDB中,可以使用mongodump和mongorestore命令来备份和恢复数据库。 备份数据: 使用mongodum...
    99+
    2024-04-09
    mongodb
  • mysql数据库恢复的方法是什么
    MySQL数据库恢复的方法有以下几种: 通过备份文件恢复:如果有定期备份数据库的文件,可以使用备份文件进行恢复。首先,停止MyS...
    99+
    2024-04-09
    mysql
  • Oracle rman异机恢复的方法是什么
    Oracle RMAN(Recovery Manager)是Oracle数据库的备份和恢复工具。RMAN提供了异机恢复的方法,可以将...
    99+
    2024-04-09
    Oracle
  • ubuntu恢复默认源的方法是什么
    要恢复Ubuntu的默认软件源,可以通过以下步骤进行操作:1. 打开终端(Ctrl+Alt+T)。2. 编辑 `/etc/apt/s...
    99+
    2023-10-12
    ubuntu
  • win11备份与恢复的方法是什么
    Windows 11 提供了多种备份和恢复方法,以下是几种常用的方法:1. 使用 Windows 備份與還原工具:Windows 1...
    99+
    2023-09-02
    win11
  • sqlserver数据库恢复的方法是什么
    SQL Server数据库的恢复方法通常有以下几种: 使用备份文件恢复:可以利用SQL Server Management St...
    99+
    2024-04-09
    sqlserver
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作