当前位置: 首页 > news >正文

【前端教程】二维数组排序实战:按需封装才是最优解——拒绝冗余,精简代码

在JavaScript开发中,“封装”是重要的编程思想,但过度封装反而会增加代码冗余。尤其是在处理二维数组排序这类场景时,“部分封装核心逻辑+灵活调用”的方式,比“全流程封装成单一函数”更能减少代码量、提升灵活性。本文将结合你的二维数组排序代码,解析“按需封装”的优势,优化实现逻辑,并总结实用的封装原则。

一、你的代码核心思路解析:部分封装的合理性

先看你的原始实现,核心逻辑已经体现了“按需封装”的智慧——只将“单个一维数组排序”封装成函数,而二维数组的遍历、数据输出等流程性逻辑直接写在主代码中,避免了不必要的函数嵌套。

1. 原始代码完整解析

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>二维数组排序</title>
<script>
window.onload = function() {// 1. 定义二维数组(每行代表一个一维数组)var v = [[11,22,6],[43,12,11],[55,22,0]];// 2. 遍历二维数组的每一行,调用排序函数(核心:仅封装单个数组排序)for(var i of v){// disiti函数:专门处理“单个一维数组排序”,不掺杂其他逻辑disiti(i);}// 3. 遍历二维数组,输出排序后的结果(流程性逻辑,不封装)for(var i of v){for(var j of i){document.write(j + "&nbsp;&nbsp;&nbsp;");}document.write("&nbsp;&nbsp;&nbsp;<br>");}
}// 4. 部分封装:仅负责“单个一维数组的冒泡排序”(从小到大)
function disiti(a){// 冒泡排序核心逻辑:对传入的一维数组a排序for(var i=0; i<a.length; i++){for(var j=0; j<i; j++){// 排序规则:从小到大(a[i] < a[j]时交换)if(a[i] < a[j]){var temp = a[i];a[i] = a[j];a[j] = temp;}}}
}
</script>
</head>
<body>
</body>
</html>

2. 你的代码优势:精准封装,拒绝冗余

你的实现精准避开了“全流程封装”的坑,核心优势有3点:

  • 封装边界清晰:只把“单个一维数组排序”这个“可复用核心逻辑”封装成disiti函数,二维数组的遍历(for(var i of v))、结果输出(document.write)等“一次性流程”直接写在主逻辑中,不用额外套函数。
  • 修改成本极低:如果要切换“从小到大”→“从大到小”排序,只需改disiti函数里的if(a[i] < a[j])if(a[i] > a[j]),不用动其他代码;如果用“全流程封装”,要么加参数判断(比如sortType: 'asc'/'desc'),要么写两个函数,代码量翻倍。
  • 可读性更高:主逻辑按“定义数组→排序→输出”的顺序线性执行,没有多层函数嵌套,新手也能一眼看懂流程;而全流程封装会把这些步骤藏在函数内部,反而增加理解成本。

二、为什么“全流程封装”反而不划算?

我们来对比“你的部分封装”和“全流程封装”的差异,就能明白为什么前者更优。

1. 全流程封装的典型写法(反例)

假设有人把“二维数组遍历+单个数组排序+结果输出”全封装成一个函数,代码会变成这样:

// 全流程封装:包含二维数组排序+输出,看似“统一”,实则冗余
function sort2DArrayAndPrint(twoDArray, sortType = 'asc') {// 步骤1:遍历二维数组,排序每一行(内置排序逻辑,不独立)for(let arr of twoDArray) {// 冒泡排序逻辑写在内部,无法单独复用for(let i=0; i<arr.length; i++) {for(let j=0; j<i; j++) {// 步骤2:处理排序方向,增加判断逻辑if(sortType === 'asc' ? arr[i] < arr[j] : arr[i] > arr[j]) {[arr[i], arr[j]] = [arr[j], arr[i]]; // 解构赋值交换}}}}// 步骤3:输出结果(内置输出逻辑,无法单独复用)for(let arr of twoDArray) {for(let item of arr) {document.write(item + "&nbsp;&nbsp;&nbsp;");}document.write("&nbsp;&nbsp;&nbsp;<br>");}
}// 调用时
window.onload = function() {var v = [[11,22,6],[43,12,11],[55,22,0]];sort2DArrayAndPrint(v, 'asc'); // 从小到大// sort2DArrayAndPrint(v, 'desc'); // 从大到小
}

2. 全流程封装的问题:3个“不灵活”

  • 逻辑耦合严重:排序逻辑、输出逻辑、二维数组遍历逻辑全揉在一个函数里,若只想“排序但不输出”(比如后续要把结果用表格展示,不用document.write),必须修改函数内部,违反“单一职责原则”。
  • 修改成本更高:若要更换排序算法(比如冒泡→快速排序),需要在函数内部大改;若要支持更多排序方向(比如按数组最后一个元素排序),会增加更多if-else判断,代码越来越臃肿。
  • 复用性反而低sort2DArrayAndPrint函数只能处理“二维数组排序+输出”的固定场景,无法单独复用“单个数组排序”逻辑(比如其他地方有个一维数组要排序,只能重写代码)。

三、你的代码优化:保留核心优势,提升易用性

你的“部分封装”思路完全正确,我们可以在此基础上做些细节优化,让代码更规范、灵活,同时不增加冗余。

优化方向:

  1. 命名语义化vtwoDArray(明确是二维数组),disitisortSingleArray(明确是“排序单个数组”),避免拼音命名,增强可读性。
  2. 支持排序方向:给sortSingleArray加一个sortType参数,支持“从小到大(asc)”和“从大到小(desc)”,不用写两个函数。
  3. 优化输出方式:用div+span替代document.write,让输出格式更规整,便于样式控制。
  4. 添加注释:给核心逻辑加注释,方便后续维护。

优化后完整代码

<!DOCTYPE html>
<html>
<head><meta charset="utf-8" /><title>优化版二维数组排序</title><style>/* 优化输出样式:用网格布局让结果更规整 */.result-container {margin: 20px;padding: 15px;border: 1px solid #eee;border-radius: 6px;}.array-row {margin: 8px 0;padding: 8px;background-color: #f8f9fa;}.array-item {display: inline-block;width: 40px;text-align: center;margin: 0 4px;padding: 4px;border: 1px solid #ddd;border-radius: 4px;}</style>
</head>
<body><div class="result-container" id="resultContainer"></div><script>window.onload = function() {// 1. 定义二维数组(命名语义化:twoDArray = 二维数组)const twoDArray = [[11, 22, 6], [43, 12, 11], [55, 22, 0]];// 2. 遍历二维数组,调用排序函数(支持指定排序方向:'asc'升序,'desc'降序)// 需求1:从小到大排序 → 传'sortSingleArray(arr, 'asc')'// 需求2:从大到小排序 → 传'sortSingleArray(arr, 'desc')'for (const arr of twoDArray) {sortSingleArray(arr, 'asc'); // 核心:复用排序函数,仅改参数即可切换方向}// 3. 输出排序结果(优化为DOM操作,替代document.write,便于样式控制)renderResult(twoDArray);}/*** 部分封装:仅负责“单个一维数组的冒泡排序”(支持升序/降序)* @param {Array} arr - 要排序的一维数组* @param {string} sortType - 排序方向:'asc'(升序,默认)、'desc'(降序)*/function sortSingleArray(arr, sortType = 'asc') {// 冒泡排序核心逻辑(仅处理单个一维数组)const len = arr.length;for (let i = 0; i < len; i++) {// 内层循环:比较相邻元素,完成一轮排序for (let j = 0; j < i; j++) {// 根据sortType判断排序规则(仅此处需要修改,其他逻辑不变)const needSwap = sortType === 'asc' ? arr[i] < arr[j] // 升序:后面的元素小则交换: arr[i] > arr[j]; // 降序:后面的元素大则交换if (needSwap) {// 交换元素(ES6解构赋值,更简洁)[arr[i], arr[j]] = [arr[j], arr[i]];}}}}/*** 辅助函数:渲染二维数组排序结果(流程性逻辑,单独封装便于修改样式)* @param {Array} twoDArray - 排序后的二维数组*/function renderResult(twoDArray) {const container = document.getElementById('resultContainer');container.innerHTML = '<h3>二维数组排序结果(升序)</h3>';// 遍历二维数组,创建DOM元素展示twoDArray.forEach((arr, rowIndex) => {const rowDiv = document.createElement('div');rowDiv.className = 'array-row';rowDiv.innerHTML = `<span>第${rowIndex + 1}行:</span>`;// 遍历一维数组,创建每个元素的展示节点arr.forEach(item => {const itemSpan = document.createElement('span');itemSpan.className = 'array-item';itemSpan.textContent = item;rowDiv.appendChild(itemSpan);});container.appendChild(rowDiv);});}</script>
</body>
</html>

优化后保留的核心优势:

  • 封装依然精简sortSingleArray只负责“单个数组排序”,renderResult只负责“结果渲染”,两个函数各司其职,没有冗余逻辑。
  • 修改依然灵活
    • 切换排序方向:只需改sortSingleArray(arr, 'asc')sortSingleArray(arr, 'desc')
    • 修改输出样式:只需调整renderResult里的CSS类或DOM结构,不用动排序逻辑;
    • 更换排序算法:只需重写sortSingleArray内部的排序逻辑(比如改成快速排序),主逻辑和渲染逻辑完全不变。

四、总结:按需封装的3个核心原则

从你的二维数组排序代码中,我们可以提炼出“按需封装”的实用原则——封装的目的是“减少重复代码”,不是“为了封装而封装”

1. 只封装“可复用的核心逻辑”

  • 可复用逻辑:比如“单个数组排序”(其他场景可能也需要排序一维数组)、“日期格式化”(多个地方需要展示日期);
  • 不封装“一次性流程”:比如“当前页面的二维数组遍历”“特定格式的结果输出”(这些逻辑只在当前场景用,封装后反而增加调用成本)。

2. 封装函数保持“单一职责”

  • 一个函数只做一件事:sortSingleArray只排序,renderResult只渲染,不要让一个函数既排序又输出,既处理数组又判断逻辑;
  • 示例:如果要给二维数组排序加“按某一列排序”(比如按每行第二个元素排序),只需新增一个sort2DArrayByColumn函数,复用sortSingleArray,而不是在原有函数里加if-else

3. 用“参数”替代“多函数”

  • 需求差异用参数控制:比如排序方向(sortType: 'asc'/'desc')、数组列索引(columnIndex: 0/1/2),不用为每个需求写一个函数;
  • 反例:不要写sort2DArrayAsc(升序)、sort2DArrayDesc(降序)两个函数,而是用一个sort2DArray加参数控制,减少代码重复。

最终结论

你的二维数组排序实现思路完全正确——部分封装核心逻辑,灵活处理流程性需求,比全流程封装更能减少代码量、提升灵活性。在实际开发中,我们不需要追求“大而全”的封装,而是要像你这样,根据需求精准判断“哪些该封装,哪些该直接写”,这才是高效的编码思路。

http://www.dtcms.com/a/357500.html

相关文章:

  • 基于SpringBoot和百度人脸识别API开发的保安门禁系统
  • MinIO社区版“背刺“之后:RustFS用Apache 2.0协议重构开源信任
  • 导入自定义模块的过程中出现ModuleNotFoundError错误
  • “白月光”焦点何晟铭现身宁夏中宁,助力非遗与三农发展
  • 37 HTB Remote 机器 - 容易
  • RV1126的OSD模块讲解
  • ArcPy 断点续跑脚本:深度性能优化指南
  • 币安创始人赵长鹏:香港需要更广泛的加密货币产品来与美国和阿联酋竞争
  • Origin绘制四元相图
  • 3-5〔OSCP ◈ 研记〕❘ WEB应用攻击▸WEB应用枚举A
  • 数据存储与SQLite数据库
  • 3 反向传播
  • C++ 线程安全初始化机制详解与实践
  • Android 打包适配15 版本(api 35)问题处理
  • 数字人 + 矩阵聚合系统源码搭建与定制化开发
  • 内网部署数据本地化,不限时的视频会议软件-BeeWorks Meet
  • 数据结构:归并排序 (Iterative Merge Sort)
  • JavaScript 基础核心知识点总结:从使用方式到核心语法
  • 不止于价格,DigitalOcean、AWS和Linode该选谁?
  • 蘑兔音乐:音乐创作板块的槿汐姑姑
  • 抗干扰、高冗余、快部署:KAXA工业无线方案赋能注塑车间稳定联网
  • OpenCV的轮廓检测
  • 手写MyBatis第41弹:MyBatis动态代理黑魔法:MapperProxy如何智能处理增删改的返回值?
  • 【完整源码+数据集+部署教程】胚胎发育阶段检测系统源码和数据集:改进yolo11-SCConv
  • 如何从 iCloud 存储中删除消息的 4 种方法
  • ubuntu24.04 QT中配置opencv4.12
  • 引力场能量为负,物质能量为正,这是在存在物质的空间中说的,如果是空无一物的空间呢,引力场能量还是负吗(或者说引力场还存在吗)
  • 2025年09月计算机二级Java选择题每日一练——第十一期
  • Vue3 kkfileview 的使用
  • Hal aidl 模板