新闻中心
J*aScript 通用排序函数设计与实现:优化重复代码模式

本文探讨了如何在j*ascript中通过设计一个通用排序函数来优化重复的排序逻辑。针对不同属性(如字符串、数字)的数组元素排序场景,文章介绍了一种结合`map`和`sort`的高阶函数方法,有效减少代码冗余,提高可维护性,并提供了详细的实现步骤和示例代码,帮助开发者构建灵活高效的排序解决方案。
1. 重复排序逻辑的挑战
在J*aScript开发中,尤其是在处理包含复杂对象的数组时,根据对象的不同属性进行排序是一个非常常见的需求。例如,在一个待办事项列表中,用户可能希望按标题、截止日期或优先级进行排序。初学者往往会为每个排序字段编写一个独立的排序函数,导致代码中出现大量相似的、重复的逻辑。
考虑以下针对待办事项数组的排序函数示例:
// 原始的按标题排序函数
function sortTitle(array) {
return array.sort((a, b) => {
const titleA = a.title.toUpperCase();
const titleB = b.title.toUpperCase();
return (titleA > titleB) - (titleA < titleB); // 简化比较
});
}
// 原始的按日期排序函数
function sortDate(array) {
return array.sort((a, b) => {
return (a.duedate > b.duedate) - (a.duedate < b.duedate); // 简化比较
});
}
// 原始的按优先级排序函数
function sortPrio(array) {
return array.sort((a, b) => {
return (a.prio > b.prio) - (a.prio < b.prio); // 简化比较
});
}这些函数的核心逻辑都是调用 array.sort() 并提供一个比较器函数,其差异仅在于访问对象属性的方式(a.title、a.duedate、a.prio)以及对字符串进行大小写转换的处理。这种重复的代码模式不仅增加了代码量,降低了可读性,也使得后期维护(例如,修改排序逻辑或添加新字段)变得繁琐且容易出错。
2. 设计通用的高阶排序函数
为了解决上述问题,我们可以设计一个通用的高阶排序函数。这个函数将接受两个参数:待排序的数组和一个“键提取函数”(key extractor function)。“键提取函数”的作用是从数组中的每个复杂对象中提取出用于比较的标量值(例如,字符串、数字),从而将排序的“策略”与排序的“机制”分离。
这种设计思想与计算机科学中的“Schwartzian transform”技术有异曲同工之妙,其核心步骤如下:
- 映射 (Map):遍历原始数组,对每个元素应用“键提取函数”,生成一个包含 [键值, 原始元素] 对的中间数组。
- 排序 (Sort):根据这些对中的“键值”对中间数组进行排序。
- 还原 (Map):从排序后的中间数组中提取出原始元素,形成最终的排序结果。
3. 通用 sortBy 函数的实现
下面是实现通用 sortBy 函数的代码:
/**
* 通用排序函数,根据指定的键提取函数对数组进行排序。
* 该函数返回一个新数组,不修改原数组。
*
* @param {Array<Object>} array 待排序的数组。
* @param {Function} keyFn 一个函数,接受数组中的一个元素,返回用于排序的键值。
* 例如:item => item.title.toUpperCase() 或 item => item.duedate。
* @returns {Array<Object>} 排序后的新数组。
*/
const sortBy = (array, keyFn) => array
// 步骤1: 映射 - 提取键值,创建 [键值, 原始元素] 对
.map(item => [keyFn(item), item])
// 步骤2: 排序 - 根据键值对对进行排序
.sort((a, b) => {
const keyA = a[0]; // 第一个元素的键值
const keyB = b[0]; // 第二个元素的键值
// 简洁的比较函数:
// 当 keyA > keyB 返回 1
// 当 keyA < keyB 返回 -1
// 当 keyA === keyB 返回 0
return (keyA > keyB) - (keyA < keyB);
})
// 步骤3: 还原 - 提取排序后的原始元素
.map(pair => pair[1]);代码解析:
- array.map(item => [keyFn(item), item]): 这一步遍历 array 中的每个 item。对于每个 item,它调用 keyFn(item) 来获取排序依据的键值,然后创建一个新的数组 [keyFn(item), item]。这个新数组的第一个元素是键值,第二个元素是原始对象。
- .sort((a, b) => (keyA > keyB) - (keyA : 这一步对 map 生成的中间数组进行排序。a 和 b 分别是 [键值, 原始元素] 形式的对。比较器函数通过 a[0] 和 b[0] 访问键值进行比较。
- keyA > keyB:如果 keyA 大于 keyB,结果为 true (转换为 1)。
- keyA
- 当 keyA > keyB 时,表达式为 1 - 0 = 1。
- 当 keyA
- 当 keyA === keyB 时,表达式为 0 - 0 = 0。
- 这种写法是 Array.prototype.sort() 比较器函数的一种简洁实现,适用于数字和字符串的升序排序。
- .map(pair => pair[1]): 排序完成后,我们只需要原始的待办事项对象,因此再次使用 map 遍历排序后的对,并只返回每个对中的第二个元素(即原始对象)。
4. 实际应用示例
假设我们有以下待办事项数据:
let todos = [
{ title: 'Buy Milk', duedate: 8, prio: 2 },
{ title: 'Learn J*aScript', duedate: 9, prio: 1 },
{ title: 'Write Report', duedate: 7, prio: 3 },
{ title: 'Read Book', duedate: 6, prio: 1 },
{ title: 'Attend Meeting', duedate: 10, prio: 2 },
];现在,我们可以使用 sortBy 函数轻松地按不同属性进行排序:
成新网络商城购物系统
使用模板与程序分离的方式构建,依靠专门设计的数据库操作类实现数据库存取,具有专有错误处理模块,通过 Email 实时报告数据库错误,除具有满足购物需要的全部功能外,成新商城购物系统还对购物系统体系做了丰富的扩展,全新设计的搜索功能,自定义成新商城购物系统代码功能代码已经全面优化,杜绝SQL注入漏洞前台测试用户名:admin密码:admin888后台管理员名:admin密码:admin888
0
查看详情
按标题排序 (字符串,忽略大小写)
// 注意:对于字符串排序,如果需要忽略大小写,应在 keyFn 中进行转换
const sortedByTitle = sortBy(todos, item => item.title.toUpperCase());
console.log("按标题排序:", sortedByTitle);
/* 输出示例 (顺序可能因实际数据而异):
[
{ title: 'Attend Meeting', duedate: 10, prio: 2 },
{ title: 'Buy Milk', duedate: 8, prio: 2 },
{ title: 'Learn J*aScript', duedate: 9, prio: 1 },
{ title: 'Read Book', duedate: 6, prio: 1 },
{ title: 'Write Report', duedate: 7, prio: 3 }
]
*/按截止日期排序 (数字)
const sortedByDate = sortBy(todos, item => item.duedate);
console.log("按日期排序:", sortedByDate);
/* 输出示例:
[
{ title: 'Read Book', duedate: 6, prio: 1 },
{ title: 'Write Report', duedate: 7, prio: 3 },
{ title: 'Buy Milk', duedate: 8, prio: 2 },
{ title: 'Learn J*aScript', duedate: 9, prio: 1 },
{ title: 'Attend Meeting', duedate: 10, prio: 2 }
]
*/按优先级排序 (数字)
const sortedByPrio = sortBy(todos, item => item.prio); console.log("按优先级排序:", sortedByPrio); /* 输出示例: [ { title: 'Learn J*aScript', duedate: 9, prio: 1 }, { title: 'Read Book', duedate: 6, prio: 1 }, { title: 'Buy Milk', duedate: 8, prio: 2 }, { title: 'Attend Meeting', duedate: 10, prio: 2 }, { title: 'Write Report', duedate: 7, prio: 3 } ] */
集成到事件监听器
在实际应用中,当用户点击不同的排序按钮时,可以这样使用 sortBy 函数:
// 假设 titleSort, dateSort, prioSort 是 DOM 元素
// cleanTodo() 和 showTodos() 是更新UI的函数
titleSort.addEventListener("click", () => {
// sortBy 返回一个新数组,因此需要重新赋值给 todos 或处理新数组
todos = sortBy(todos, item => item.title.toUpperCase());
cleanTodo(); // 清理旧的UI显示
showTodos(); // 渲染排序后的待办事项
});
dateSort.addEventListener("click", () => {
todos = sortBy(todos, item => item.duedate);
cleanTodo();
showTodos();
});
prioSort.addEventListener("click", () => {
todos = sortBy(todos, item => item.prio);
cleanTodo();
showTodos();
});5. 优势与注意事项
优势:
- 代码优化与可维护性: 显著减少了代码冗余,将多种排序逻辑抽象为一个通用函数,使得代码更简洁、易于理解和维护。
- 灵活性与可扩展性: 开发者可以轻松地添加新的排序字段,只需提供一个新的 keyFn 即可,无需修改 sortBy 函数本身或编写新的排序函数。
- 纯函数特性: sortBy 函数返回一个新数组,不修改原始数组,这符合函数式编程的原则,有助于避免副作用,提高代码的预测性。
注意事项:
- 性能考量: sortBy 函数内部使用了两次 map 和一次 sort。对于非常大的数据集(例如,数万甚至数十万条数据),创建中间数组可能会带来一定的内存开销和计算时间。然而,在大多数Web应用场景下,这种性能影响通常可以忽略不计。
- 复杂排序逻辑: 对于需要多级排序(例如,先按优先级升序,再按日期降序)或更复杂的比较规则,keyFn 可能需要返回一个复合键(例如,一个包含多个排序字段的数组),或者 sort 比较器本身需要进行更精细的定制。
- 数据类型兼容性: 确保 keyFn 返回的值是可比较的类型(如数字、字符串)。如果属性可能为 null 或 undefined,keyFn 或比较器需要额外处理这些边界情况,以避免意外行为。
6. 总结
通过设计和实现一个通用的 sortBy 高阶函数,我们成功地将重复的排序逻辑抽象化,大大提升了代码的复用性和可维护性。这种模式不仅适用于排序,也体现了J*aScript中高阶函数在实现灵活、可扩展和优雅代码方面的强大能力。在面对相似且重复的代码模式时,开发者应积极思考如何利用抽象和参数化来构建更通用、更健壮的解决方案。
以上就是J*aScript 通用排序函数设计与实现:优化重复代码模式的详细内容,更多请关注其它相关文章!
# java
# javascript
# 高阶
# 购物系统
# 成新
# 键值
# javascript开发
# 键值对
# ios
# ai
# 计算机
# 夏季饮品营销推广
# 刷关键词排名表现易速达
# 云南企业网站建设教程
# 网站建设优化价格
# 阿里优化购票网站
# 唐山营销推广中心电话地址
# 宁波市推广总站网站
# 吴韦朋seo
# 保定市公司网站建设
# seo乙方公司
# 第一个
# 升序
# 自定义
# 第二个
# 遍历
# 有哪些
相关栏目:
【
科技资讯46185 】
【
网络学院92790 】
相关推荐:
QQ邮箱网页版入口 QQ邮箱官方邮箱登录通道
C++如何实现单例模式_C++设计模式之线程安全的单例写法
Golang如何使用new_Go new分配内存机制讲解
Go RPC HTTP服务正确实现与常见陷阱解析
将JSON对象数组转置为键值对列表的实用指南
Pyrogram与g4f集成:异步编程实践与常见错误解决
绝地鸭卫平a核爆刀流玩法攻略
C++ map遍历方法大全_C++ map迭代器使用总结
CSS响应式网页如何实现主次模块比例自适应_flex-grow与flex-shrink调整
Word2013如何插入视频和音频媒体_Word2013媒体插入的多媒体支持
在J*a中如何在J*a中使用异常机制记录错误日志_异常日志实践经验
如何在 Excel Online 和 Google 表格中更改日期格式
如何将一个大型PHP应用拆分为多个Composer包_微服务与模块化架构的Composer实践
c++中的const_cast和reinterpret_cast怎么用_c++四种类型转换
Mudbox图层蒙版怎么用_Mudbox图层蒙版数字雕刻应用技巧
微信怎么把收藏的内容分类管理 微信收藏内容标签分类方法
随机参数递归函数的基准调用次数与时间复杂度探究
微信网页版官方入口教程 微信网页版网页版快速登录步骤
Win11怎么开启高性能模式_Windows 11电源计划优化设置
移动端XML文件怎么转换成Excel 手机和平板上的解决方案
解决Python单元测试中Mock异常方法调用计数为零的问题
Basecamp怎样用留言钉固定重点_Basecamp用留言钉固定重点【重点标记】
J*aScript中向JSON对象添加新属性的正确姿势
抖音网页版平台入口 抖音网页版官网在线访问教程
qq邮箱发邮件给国外发不出去_QQ邮箱国际邮件发送失败原因与解决
网易大神账号申诉需要多久_网易大神账号申诉流程说明
Golang如何实现简单的Web表单_Golang表单提交与验证处理方法
J*aScript中安全有效地处理localStorage字符串数据
在VS Code中配置和运行Dart程序的完整步骤
单12V-2×6实现为RTX 5090供电750W!甚至都没敢跑分
J*aScript数组对象转换:按指定键分组与值收集
Lar*el Excel导入时生成自定义递增ID的策略与实践
如何在更新Composer依赖后自动运行测试_使用post-update-cmd钩子触发PHPUnit
蛙漫正版漫画平台入口_蛙漫免费阅读全站漫画资源
PHP表单数据传递:如何通过隐藏输入字段获取动态ID
Mac终端命令大全_Mac常用Terminal指令速查
钉钉视频会议画面卡顿如何解决 钉钉会议画面优化方法
AO3官方镜像站点汇总 AO3同人作品网页版直达链接
内存疯狂猛猛涨价:主板销量直接腰斩!
必由学官方登录入口 必由学教师学生账号快速访问
windows10怎么查看本机ip_windows10命令提示符ipconfig使用
为什么简单的XML文件也会解析失败? 检查隐藏的非打印字符(如BOM)的方法
c++如何实现一个简单的ECS框架_c++数据驱动设计与游戏开发
如何在CSS中使用浮动制作导航栏_float实现水平菜单
高德地图沿途添加点失败如何解决 高德多点规划方法
C#使用XPath查询节点时出错? 常见语法错误与调试技巧
vivo云服务网页版登录 怎么登录vivo云服务网页版
Flexbox布局实践:实现粘性导航栏与底部固定页脚
微信网页版官方快速登录入口 微信网页版网页版账号直达
TikTok国际版官网直达_TikTok国际版官网直达进入在线观看


2025-11-07
浏览次数:次
返回列表
log("按优先级排序:", sortedByPrio);
/* 输出示例:
[
{ title: 'Learn J*aScript', duedate: 9, prio: 1 },
{ title: 'Read Book', duedate: 6, prio: 1 },
{ title: 'Buy Milk', duedate: 8, prio: 2 },
{ title: 'Attend Meeting', duedate: 10, prio: 2 },
{ title: 'Write Report', duedate: 7, prio: 3 }
]
*/