讲师中心 微信公众号
AI工具推荐 视频效率加速

JavaScript代码重构:优化重复逻辑与提升可维护性

夜杰酱_5100

夜杰酱_5100

发布时间:2025-11-25 11:14:38

|

771人浏览过

|

来源于php中文网

原创

JavaScript代码重构:优化重复逻辑与提升可维护性

本文旨在探讨如何通过数据驱动、事件委托和函数封装等策略,对前端javascript代码中重复的ui交互逻辑进行重构。通过将元素配置数据化,并利用事件委托机制集中处理事件,结合一系列通用辅助函数,可以显著减少代码量,提高代码的可读性、可维护性和可扩展性,从而构建更健壮、更易于管理的前端应用。

在前端开发中,我们经常会遇到需要处理多个相似UI元素交互逻辑的场景。例如,页面上有多个输入框和对应的提交按钮,每个按钮点击后都需要根据输入框的值执行相似的校验和UI更新操作。如果为每个元素都编写独立的事件监听器和处理逻辑,代码将变得冗余、难以维护,并且在需求变更或元素增多时,修改成本会急剧上升。

问题分析:重复代码的挑战

考虑以下初始代码示例,它展示了为两个独立的输入框和按钮对编写的重复逻辑:

const an1 = document.getElementById("an1");
const bt1 = document.getElementById("bt1");

bt1.addEventListener("click", () => {
  if (an1.value.toLowerCase() === "test") {
    document.getElementById("bt1").style.display = "none";
    document.getElementById("an1").style.display = "none";
    document.getElementById("wo1").style.display = "initial";
  } else {
    bt1.innerText = "Wrong!";
    document.getElementById("bt1").style.background = "red";
  }
});

const an2 = document.getElementById("an2");
const bt2 = document.getElementById("bt2");

bt2.addEventListener("click", () => {
  if (an2.value.toLowerCase() === "test1") {
    document.getElementById("bt2").style.display = "none";
    document.getElementById("an2").style.display = "none";
    document.getElementById("wo2").style.display = "initial";
  } else {
    bt2.innerText = "Wrong!";
    document.getElementById("bt2").style.background = "red";
  }
});

以及对应的HTML结构:

<tr>
  <td class="c1"><input id="an1" placeholder="test" type="text" onblur="this.value=removeSpaces(this.value);" /><a id="wo1" style="display: none;">test</a><button id="bt1">Submit</button></td>
  <td class="c1">test</td>
</tr>
<tr>
  <td class="c2"><input id="an2" placeholder="test1" type="text" onblur="this.value=removeSpaces(this.value);" /><a id="wo2" style="display: none;">test1</a><button id="bt2">Submit</button></td>
  <td class="c2">test1</td>
</tr>

这段代码的问题显而易见:

立即学习“Java免费学习笔记(深入)”;

  1. 代码重复:bt1 和 bt2 的点击事件处理逻辑高度相似,仅在元素ID和预期值上有所不同。
  2. 可维护性差:如果需要修改校验逻辑或UI更新方式,必须在多处进行修改。
  3. 可扩展性差:当页面上出现更多的 anX/btX 组合时,需要复制粘贴大量代码,导致文件膨胀。

重构策略:数据驱动与事件委托

为了解决上述问题,我们可以采用以下核心重构策略:

  1. 数据结构化:将所有与UI元素及其行为相关的信息(如ID、预期值、显示状态等)存储在易于管理的数据结构中,例如数组或对象。
  2. 事件委托:利用事件冒泡机制,在父元素上设置一个事件监听器,而不是为每个子元素单独设置,从而集中管理事件。
  3. 函数封装:将通用的DOM操作和逻辑判断封装成独立的辅助函数,提高代码的复用性。

1. 数据结构化:配置化管理UI元素

首先,我们将每个输入框的预期答案和每个按钮点击后需要改变显示状态的元素及其目标状态,分别定义为数组对象。

const expectedElementAnswer = [
  { id: 'an1', expectAns: 'test' },
  { id: 'an2', expectAns: 'test1' }
];

const btnElementList = [
  [{ id: 'bt1', displayValue: 'none' }, { id: 'an1', displayValue: 'none' }, { id: 'wo1', displayValue: 'initial' }],
  [{ id: 'bt2', displayValue: 'none' }, { id: 'an2', displayValue: 'none' }, { id: 'wo2', displayValue: 'initial' }]
];
  • expectedElementAnswer 数组存储了每个输入框的ID及其对应的正确答案。
  • btnElementList 是一个二维数组,每个子数组对应一个按钮点击后需要操作的一组元素。每个对象包含元素的ID和其目标 display 样式值。这种配置化的方式使得添加或修改元素行为变得极其简单,只需更新这些数组即可。

2. 事件委托:简化事件处理

我们将事件监听器附加到所有相关按钮的共同父元素上(例如一个

元素),而不是每个按钮。当点击事件发生时,通过 event.target 判断是哪个按钮被点击,然后执行相应的逻辑。
document.getElementById('myTable').addEventListener('click', (event) => {
  const curElemetId = event.target.id;
  // 根据点击的按钮ID确定是哪一组逻辑
  const btnIndex = (curElemetId === 'bt1') ? 1 : (curElemetId === 'bt2') ? 2 : -1;

  if ((btnIndex === 1) || (btnIndex === 2)) {
    const idx = btnIndex - 1; // 转换为数组索引
    const { id, expectAns } = expectedElementAnswer[idx];

    if (getElementValue(id).toLowerCase() === expectAns) {
      changeDisplayStyles(idx);
    } else {
      setErrorWarning(curElemetId);
    }
  }
});
  • myTable 是包含所有输入框和按钮的父级
元素的ID。
  • event.target.id 获取实际被点击元素的ID。
  • 通过判断 curElemetId,我们可以确定是 bt1 还是 bt2 被点击,从而获取对应的配置数据索引 idx。
  • 3. 封装通用操作:提升代码复用性

    为了进一步减少重复并提高可读性,我们将常用的DOM操作和数据获取逻辑封装成独立的函数:

    function changeDisplayStyles(idx) {
      btnElementList[idx].forEach(({ id, displayValue }) => {
        setDisplayStyle(id, displayValue)
      });
    }
    
    function setDisplayStyle(displayId, value) {
      document.getElementById(displayId).style.display = value;
    }
    
    function setErrorWarning(displayId, bgColor = 'red') {
      const element = document.getElementById(displayId);
      element.innerText = 'Wrong!';
      element.style.background = bgColor;
    }
    
    function getElementValue(id) {
      const value = document.getElementById(id)?.value || '';
      return value;
    }
    • changeDisplayStyles(idx):根据索引 idx 从 btnElementList 中获取需要改变样式的元素列表,并遍历调用 setDisplayStyle。
    • setDisplayStyle(displayId, value):一个通用的函数,用于设置指定ID元素的 display 样式。
    • setErrorWarning(displayId, bgColor):用于显示错误提示,修改按钮文本和背景色。
    • getElementValue(id):安全地获取指定ID输入框的值,处理了元素可能不存在的情况。

    完整重构代码示例

    结合上述策略,以下是完整的JavaScript和HTML代码:

    // change below arrays if you need this functionality for more fields
    
    const expectedElementAnswer = [{ id: 'an1', expectAns: 'test' }, { id: 'an2', expectAns: 'test1' }];
    const btnElementList = [
            [{ id: 'bt1', displayValue: 'none' },{ id: 'an1', displayValue: 'none' }, { id: 'wo1', displayValue: 'initial' }],
            [{ id: 'bt2', displayValue: 'none' }, { id: 'an2', displayValue: 'none' }, { id: 'wo2', displayValue: 'initial' }]
    ];
    
    document.getElementById('myTable').addEventListener('click', (event) => {
          const curElemetId = event.target.id;
          // 这里的逻辑可以进一步优化,例如使用数据驱动的查找,而非硬编码的if-else if
          const btnIndex = (curElemetId === 'bt1') ? 1 : (curElemetId === 'bt2') ? 2 : -1;
    
          if ((btnIndex === 1) || (btnIndex === 2)) {
            const idx = btnIndex - 1;
            const { id, expectAns } = expectedElementAnswer[idx];
    
            if (getElementValue(id).toLowerCase() === expectAns) {
              changeDisplayStyles(idx);
            } else {
              setErrorWarning(curElemetId);
            }
          }
    });
    
    function changeDisplayStyles(idx) {
          btnElementList[idx].forEach(({ id, displayValue }) => {
            setDisplayStyle(id, displayValue)
          });
    }
    
    function setDisplayStyle(displayId, value) {
          document.getElementById(displayId).style.display = value;
    }
    
    function setErrorWarning(displayId, bgColor = 'red') {
          const element = document.getElementById(displayId);
          element.innerText = 'Wrong!';
          element.style.background = bgColor;
    }
    
    function getElementValue(id) {
          const value = document.getElementById(id)?.value || '';
          return value;
    }

    对应的HTML结构,注意父级

    元素现在有了 id="myTable":
    <table id="myTable">
    <tr>
        <td class="c1">
            <input id="an1" placeholder="test" type="text"/>
            <a id="wo1" style="display: none;">test</a>
            <button id="bt1">Submit</button>
        </td>
        <td class="c1">test</td>
      </tr>
    
      <tr>
        <td class="c2">
            <input id="an2" placeholder="test1" type="text"/>
            <a id="wo2" style="display: none;">test1</a>
            <button id="bt2">Submit</button>
        </td>
        <td class="c2">test1</td>
      </tr>
    </table>

    注意事项与最佳实践

    1. 可扩展性优化:当前的 btnIndex 判断仍是硬编码的 if-else if 结构。为了更好的可扩展性,可以进一步优化为通过遍历 expectedElementAnswer 或 btnElementList 来动态查找 idx,例如:

      const btnMap = new Map();
      expectedElementAnswer.forEach((item, index) => {
        // 假设每个expectedElementAnswer的索引对应btnElementList的索引
        // 且btnElementList[index]的第一个元素是对应的按钮
        btnMap.set(btnElementList[index][0].id, index);
      });
      
      document.getElementById('myTable').addEventListener('click', (event) => {
        const curElementId = event.target.id;
        const idx = btnMap.get(curElementId); // 直接获取索引
      
        if (idx !== undefined) { // 确保是需要处理的按钮
          const { id, expectAns } = expectedElementAnswer[idx];
          // ... 后续逻辑不变
        }
      });

      这样,当添加新的输入/按钮对时,只需更新 expectedElementAnswer 和 btnElementList 数组,无需修改事件处理逻辑。

    2. 错误处理与用户反馈:当前的错误提示仅限于改变按钮文本和背景色。在实际应用中,可以考虑更友好的用户反馈方式,例如在输入框下方显示错误信息、使用动画效果等。

    3. 性能考量:事件委托对于包含大量可交互元素的列表或表格尤其有效,因为它减少了需要注册的事件监听器数量,从而降低了内存占用和提高页面性能。

    4. 代码可读性:清晰的变量命名(如 expectedElementAnswer, btnElementList)和函数命名(如 changeDisplayStyles, setErrorWarning)对于提升代码可读性至关重要。

    5. 解耦HTML与JavaScript:避免在HTML中直接使用 onblur 等行内事件处理器,将所有事件逻辑集中在JavaScript中处理,可以更好地分离结构与行为。

    总结

    通过本教程中介绍的重构方法,我们将原本重复且难以维护的代码转换为一个结构清晰、高度可配置和易于扩展的解决方案。核心思想是将数据与逻辑分离,利用数据结构来定义UI元素的行为,并通过事件委托和函数封装来集中管理和复用交互逻辑。这种模式不仅减少了代码量,还显著提升了应用的可维护性和可扩展性,为构建高质量的前端应用奠定了基础。

    热门AI工具

    更多
    墨刀AI
    墨刀AI Hot

    一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

    DeepSeek

    DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

    小橡皮AI

    一款AI工具,主要用于小橡皮一键去AI味,将机器味重的内容改得更有活人感,防止因为 AI 内容被平台打上 AI 标签,以及限流,适合需要提升相关任务效率的用户。

    超级麦吉

    一款通过数字员工协作完成调研、数据分析与文档制作的AI办公平台。

    Atoms
    Atoms Hot

    Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

    豆包大模型

    豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

    WorkBuddy

    一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

    立刻MV
    立刻MV Hot

    立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

    UpDream
    UpDream Hot

    一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

    相关专题

    更多
    treenode的用法
    treenode的用法

    ​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

    2661

    2023.12.01

    C++ 高效算法与数据结构
    C++ 高效算法与数据结构

    本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

    376

    2025.12.22

    深入理解算法:高效算法与数据结构专题
    深入理解算法:高效算法与数据结构专题

    本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

    417

    2026.01.06

    C++ 数据结构与算法实现教程合集
    C++ 数据结构与算法实现教程合集

    以 C++ 为实现语言,系统讲解核心数据结构与算法,涵盖链表(单链表/双链表/环检测)、栈与队列(单调栈/优先队列)、二叉树(遍历/BST/AVL/红黑树)、哈希表(开地址法/链地址法)、图(邻接表/BFS/DFS/Dijkstra/拓扑排序)、常见排序算法(快排/归并/堆排/计数排序)的实现与复杂度分析,同时分享 LeetCode 刷题技巧、竞赛编程常用模板(二分/前缀和/滑动窗口/动态规划),帮助开发者夯实算法基础。

    472

    2026.05.09

    DOM是什么意思
    DOM是什么意思

    dom的英文全称是documentobjectmodel,表示文件对象模型,是w3c组织推荐的处理可扩展置标语言的标准编程接口;dom是html文档的内存中对象表示,它提供了使用javascript与网页交互的方式。想了解更多的相关内容,可以阅读本专题下面的文章。

    6521

    2024.08.14

    Gradle插件开发入门教程
    Gradle插件开发入门教程

    本专题介绍Gradle插件开发的基本流程,涵盖插件类编写、自定义任务注册、扩展参数设计和插件应用方法,帮助你理解插件如何组织构建逻辑,减少多个项目之间的重复配置。

    120

    2026.10.22

    Gradle Wrapper配置教程
    Gradle Wrapper配置教程

    本专题介绍Gradle Wrapper的基本作用,涵盖文件组成、生成方法、版本配置和任务调用,帮助你理解项目如何使用指定的Gradle版本,减少不同开发环境带来的构建差异。

    60

    2026.10.22

    Golangci-lint检查器使用教程
    Golangci-lint检查器使用教程

    本专题整理Golangci-lint常用Linters的用途与配置方法,覆盖错误处理、无效代码、安全、复杂度、命名和依赖检查,帮助项目合理选规则。

    60

    2026.10.22

    Gradle怎么构建Java项目
    Gradle怎么构建Java项目

    本专题介绍Gradle Java项目构建方法,涵盖Java插件配置、依赖声明、源码编译、测试执行和打包检查,帮助你理解各阶段的任务关系,逐步完成项目构建。

    40

    2026.10.22

    热门下载

    更多
    网站特效
    /
    网站源码
    /
    网站素材
    /
    前端模板

    精品课程

    更多
    相关推荐
    /
    热门推荐
    /
    最新课程
    WebStorm 官方调试文档
    WebStorm 官方调试文档

    共0课时 | 0人学习

    React 教程
    React 教程

    共58课时 | 12.3万人学习

    TypeScript 教程
    TypeScript 教程

    共19课时 | 6.7万人学习

    关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
    php中文网:公益在线php培训,帮助PHP学习者快速成长!
    关注服务号
    PHP中文网订阅号
    每天精选资源文章推送

    Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn