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 是单一职责原则的很好体现,它专注于解决一个特定问题:对已排序数组进行去重。这种针对特定场景的优化是高性能库的重要特征。

相关推荐
五号厂房5 分钟前
仿照AntDesign,实现一个自定义Tab
前端
Bob999813 分钟前
三大浏览器(Firefox、Opera、Chrome)多个Profile管理!
开发语言·javascript·eclipse·sqlite·ecmascript·hbase
Frankabcdefgh20 分钟前
前端面试 js
开发语言·javascript·原型模式
浏览器爱好者30 分钟前
如何删除Google Chrome中的所有历史记录【一键清除】
前端·chrome
埃兰德欧神31 分钟前
三分钟让你的H5变身‘伪原生’,揭秘H5秒变应用的魔法配置
javascript·html·产品
米开朗基杨32 分钟前
Cursor 最强竞争对手来了,专治复杂大项目,免费一个月
前端·后端
Lonwayne33 分钟前
Web服务器技术选型指南:主流方案、核心对比与策略选择
运维·服务器·前端·程序那些事
学习机器不会机器学习40 分钟前
深入浅出JavaScript常见设计模式:从原理到实战(1)
开发语言·javascript·设计模式
hax44 分钟前
deepseek-R1 理解代码能力一例
javascript·deepseek
brzhang1 小时前
效率神器!TmuxAI:一款无痕融入终端的AI助手,让我的开发体验翻倍提升
前端·后端·算法