Lodash源码阅读-baseSortedUniq

Lodash 源码阅读-baseSortedUniq

概述

baseSortedUniq 是 Lodash 内部的一个基础函数,用于对已排序数组进行去重操作。它是 _.sortedUniq_.sortedUniqBy 这两个公开 API 的底层实现。与普通的 baseUniq 相比,baseSortedUniq 针对已排序数组进行了优化,能够以线性时间复杂度(O(n))完成去重操作,效率更高。

前置学习

依赖函数

  • eq: 用于比较两个值是否相等,处理包括 NaN 在内的特殊情况
  • iteratee: 可选的迭代器函数,用于在比较前转换元素

技术知识

  • 排序数组的特性:相同元素在排序后必定相邻
  • JavaScript 中的 NaN 和 +0/-0 特殊值处理
  • 线性扫描算法
  • 函数式编程中的迭代器模式

源码实现

javascript 复制代码
function baseSortedUniq(array, iteratee) {
  var index = -1,
    length = array.length,
    resIndex = 0,
    result = [];

  while (++index < length) {
    var value = array[index],
      computed = iteratee ? iteratee(value) : value;

    if (!index || !eq(computed, seen)) {
      var seen = computed;
      result[resIndex++] = value === 0 ? 0 : value;
    }
  }
  return result;
}

实现思路

baseSortedUniq 函数实现的思路非常直接明了,利用了排序数组的特性:相同元素在排序后必定相邻。函数通过线性扫描数组,只需要将每个元素与前一个元素比较,如果不相同则保留,从而实现去重。具体步骤:

  1. 初始化必要的变量:数组指针、结果数组索引等
  2. 线性扫描排序数组的每个元素
  3. 对每个元素应用可选的迭代器函数
  4. 将当前元素与上一个保留的元素比较
  5. 如果不相同(或是第一个元素),则保留当前元素
  6. 返回去重后的结果数组

整个算法只需要一次遍历,时间复杂度为 O(n),非常高效。

源码解析

让我们逐行分析 baseSortedUniq 函数的实现:

javascript 复制代码
function baseSortedUniq(array, iteratee) {

函数定义,接收两个参数:

  • array: 要去重的已排序数组
  • iteratee: 可选的迭代器函数,用于转换数组元素后再进行比较
javascript 复制代码
var index = -1,
  length = array.length,
  resIndex = 0,
  result = [];

初始化变量:

  • index = -1: 数组遍历指针,从 -1 开始是因为用前置自增(++index
  • length = array.length: 缓存数组长度,避免循环中重复访问
  • resIndex = 0: 结果数组的索引指针,用于高效添加元素
  • result = []: 存储去重结果的数组
javascript 复制代码
while (++index < length) {

使用 while 循环遍历数组,每次将 index 自增后与数组长度比较。

javascript 复制代码
var value = array[index],
  computed = iteratee ? iteratee(value) : value;

在每次循环中:

  • 获取当前元素的值 value
  • 如果提供了迭代器函数,则应用迭代器获取计算后的值 computed;否则使用原值
javascript 复制代码
if (!index || !eq(computed, seen)) {

这是关键的去重判断逻辑:

  • !index: 如果是第一个元素(index 为 0),直接保留
  • !eq(computed, seen): 如果当前计算值与上一个保留的计算值不相等,也保留

eq 函数会处理 JavaScript 中的特殊情况,如 NaN 与 NaN 相等比较等。

javascript 复制代码
var seen = computed;

更新 seen 变量,记录最近一个计算值,用于下一轮比较。

javascript 复制代码
result[resIndex++] = value === 0 ? 0 : value;

将当前元素添加到结果数组,并同时自增结果索引。这里有一个特殊处理:

  • value === 0 ? 0 : value: 处理 +0 和 -0,确保统一返回 +0(数字 0)

这样处理是为了保持与 SameValueZero 比较一致,将 +0 和 -0 视为相同值。

javascript 复制代码
return result;

返回去重后的结果数组。

特殊值处理分析

1. 第一个元素的处理
javascript 复制代码
if (!index || !eq(computed, seen)) {

由于 index 初始为 -1,第一次循环中 index 自增后为 0,!index 为 true,所以第一个元素总是被保留,不需要与前值比较。

2. NaN 值的处理

通过 eq 函数,baseSortedUniq 可以正确处理 NaN 值。在标准 JavaScript 中,NaN !== NaN,但在去重操作中我们期望将 NaN 视为相等的值。eq 函数内部使用 SameValueZero 算法,能够正确识别两个 NaN 值相等。

3. +0/-0 的处理
javascript 复制代码
result[resIndex++] = value === 0 ? 0 : value;

JavaScript 中存在 +0 和 -0 两种零值,它们在 === 比较中被视为相等,但在某些操作(如 1/+01/-0)中有区别。baseSortedUniq 函数确保在结果中统一使用 +0,这是通过 value === 0 ? 0 : value 实现的。

总结

baseSortedUniq 函数是一个专门针对已排序数组设计的高效去重实现,主要特点包括:

  1. 线性时间复杂度:只需 O(n) 的时间完成去重,比一般去重算法更高效
  2. 空间效率:只需 O(1) 的额外空间(不计算结果数组)
  3. 迭代器支持:可以通过迭代器函数在比较前转换元素,增加了灵活性
  4. 特殊值处理:正确处理 JavaScript 中的 NaN 和 +0/-0 等特殊值

与通用的 baseUniq 相比,baseSortedUniq 更简洁、更高效,但要求输入数组必须已预先排序。这体现了 Lodash 库的设计思想:为不同场景提供专门优化的实现,在保证功能正确的同时追求最佳性能。

从软件工程角度看,baseSortedUniq 是单一职责原则的很好体现,它专注于解决一个特定问题:对已排序数组进行去重。这种针对特定场景的优化是高性能库的重要特征。

相关推荐
繁依Fanyi36 分钟前
用 CodeBuddy 实现「IdeaSpark 每日灵感卡」:一场 UI 与灵感的极简之旅
开发语言·前端·游戏·ui·编辑器·codebuddy首席试玩官
来自星星的坤3 小时前
【Vue 3 + Vue Router 4】如何正确重置路由实例(resetRouter)——避免“VueRouter is not defined”错误
前端·javascript·vue.js
香蕉可乐荷包蛋7 小时前
浅入ES5、ES6(ES2015)、ES2023(ES14)版本对比,及使用建议---ES6就够用(个人觉得)
前端·javascript·es6
未来之窗软件服务7 小时前
资源管理器必要性———仙盟创梦IDE
前端·javascript·ide·仙盟创梦ide
liuyang___8 小时前
第一次经历项目上线
前端·typescript
西哥写代码9 小时前
基于cornerstone3D的dicom影像浏览器 第十八章 自定义序列自动播放条
前端·javascript·vue
清风细雨_林木木9 小时前
Vue 中生成源码映射文件,配置 map
前端·javascript·vue.js
FungLeo9 小时前
node 后端和浏览器前端,有关 RSA 非对称加密的完整实践, 前后端匹配的代码演示
前端·非对称加密·rsa 加密·node 后端
雪芽蓝域zzs9 小时前
JavaScript splice() 方法
开发语言·javascript·ecmascript
不灭锦鲤9 小时前
xss-labs靶场第11-14关基础详解
前端·xss