Lodash源码阅读-uniqWith

Lodash 源码阅读-uniqWith

概述

uniqWith 函数用于创建一个去重后的数组,通过提供一个自定义比较器函数来判断元素是否相同。它与基本的 uniq 函数不同,后者使用 SameValueZero 内置比较方式,而 uniqWith 允许开发者自定义比较逻辑,使其更加灵活。结果数组中元素的顺序由它们在原数组中首次出现的顺序决定。

前置学习

依赖函数

  • baseUniq: 实现数组去重的基础函数,接收数组、迭代器和比较器参数。

技术知识

  • 函数参数验证: 检查参数类型和有效性
  • 函数式编程: 提供自定义比较器作为参数
  • JavaScript 的数组操作
  • 对象比较策略: 如何比较引用类型的值

源码实现

js 复制代码
function uniqWith(array, comparator) {
  comparator = typeof comparator == "function" ? comparator : undefined;
  return array && array.length ? baseUniq(array, undefined, comparator) : [];
}

实现思路

uniqWith 的实现非常简洁,主要分为三个步骤:

  1. 验证 comparator 参数是否为函数,如果不是则设为 undefined
  2. 检查 array 是否存在且有长度,如果是则进行去重处理
  3. 调用 baseUniq 进行实际的去重操作,传入 arrayundefined (表示不需要迭代器) 和 comparator

核心去重逻辑由 baseUniq 函数实现,uniqWith 只是封装了参数验证和基础处理,使 API 更加清晰易用。

源码解析

我们来逐行分析 uniqWith 函数的源码:

js 复制代码
function uniqWith(array, comparator) {

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

  • array: 要去重的数组
  • comparator: 用于比较数组元素的函数,接收两个参数 (arrVal, othVal)
js 复制代码
comparator = typeof comparator == "function" ? comparator : undefined;

这行代码检查 comparator 参数的类型:

  • 如果是函数类型,保持不变
  • 如果不是函数类型,设置为 undefined

这种处理确保了传递给 baseUniqcomparator 要么是一个合法的函数,要么是 undefined。不符合预期的比较器值(如非函数值)不会被错误地应用。

js 复制代码
return array && array.length ? baseUniq(array, undefined, comparator) : [];

这行代码执行两个关键操作:

  1. 首先检查 array 是否存在且有长度(array && array.length):

    • 如果条件为真,调用 baseUniq(array, undefined, comparator) 进行去重
    • 如果条件为假(数组不存在或为空),直接返回空数组 []
  2. 调用 baseUniq 时传递了三个参数:

    • array: 原始数组
    • undefined: 表示不使用迭代器(第二个参数为 iteratee,在 uniqWith 中不需要)
    • comparator: 自定义比较器函数,用于判断两个元素是否相同

当我们调用 baseUniq 并传入比较器时,baseUniq 内部会做以下处理:

  1. 设置 isCommon = false,表明我们正在使用非标准的比较方式
  2. 使用 arrayIncludesWith 而不是默认的 arrayIncludes 作为包含检查函数,因为需要支持自定义比较器
  3. 对每个数组元素,使用比较器函数检查是否已在结果集中,避免重复

一个不那么直观但很重要的细节是,当使用比较器时,baseUniq 会跳过针对大数组的 Set 优化路径。这是因为 Set 使用内置的 SameValueZero 比较,无法应用自定义比较器。

示例分析

以官方示例为例:

js 复制代码
var objects = [
  { x: 1, y: 2 },
  { x: 2, y: 1 },
  { x: 1, y: 2 },
];
_.uniqWith(objects, _.isEqual);
// => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]

处理流程如下:

  1. 验证 comparator (这里是 _.isEqual) 是函数类型,保持不变
  2. 验证 array 存在且有长度,调用 baseUniq
  3. baseUniq 使用 arrayIncludesWith 配合 _.isEqual 进行比较
  4. 对数组中的第三个对象 { 'x': 1, 'y': 2 },使用 _.isEqual 与已有结果比较
  5. _.isEqual 发现它与第一个对象 { 'x': 1, 'y': 2 } 深度相等(即使它们是不同的引用)
  6. 忽略这个重复元素,最终返回 [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }]

如果没有使用 _.isEqual 而是使用普通的相等比较,则三个对象会被视为不同对象(因为它们是不同的引用),结果将保留所有三个对象。

总结

uniqWith 函数是 Lodash 中一个强大的数组去重工具,它的主要特点是:

  1. 灵活性:通过自定义比较器,可以根据任意规则判断元素是否相同,特别适合处理复杂对象
  2. 易用性:简单的 API 设计,只需传入数组和比较器即可
  3. 健壮性:对参数进行充分验证,确保函数能安全应用于各种输入情况

在实际开发中,uniqWith 常用于以下场景:

  1. 去除对象数组中"业务逻辑上"相同的对象,即使它们是不同的引用
  2. 实现自定义相等逻辑的去重,例如忽略某些属性或进行模糊匹配
  3. 结合 _.isEqual 等工具函数,实现深度相等比较的去重

从软件设计角度看,uniqWith 体现了单一职责原则和开放封闭原则。它专注于数组去重的单一职责,并通过比较器参数对不同去重策略开放,同时封装了内部实现细节,提供了一个简洁一致的 API。

相关推荐
Rudon滨海渔村几秒前
【Tauri】桌面程序exe开发 - Tauri+Vue开发Windows应用 - 比Electron更轻量!8MB!
javascript·electron·tauri·桌面应用
cg50175 分钟前
Vue回调函数中的this
前端·javascript·vue.js
前端太佬7 分钟前
从零到一实现扫码登录:一个前端菜鸟的踩坑实录
前端·javascript·架构
yuanmenglxb200417 分钟前
微信小程序核心技术栈
前端·javascript·vue.js·笔记·微信小程序·小程序
爱编程的鱼18 分钟前
如何让 HTML 文件嵌入另一个 HTML 文件:详解与实践
前端·html
_092722 分钟前
Vue 2 与 Vue 3 的核心区别及 Vue 3 新特性详解
前端
David凉宸24 分钟前
一文带你使用Vue完成移动端(apk)项目
前端
纪元A梦33 分钟前
华为OD机试真题——绘图机器(2025A卷:100分)Java/python/JavaScript/C++/C/GO最佳实现
java·javascript·c++·python·华为od·go·华为od机试题
会飞的鱼先生36 分钟前
Vue3的内置组件 -实现过渡动画 TransitionGroup
前端·javascript·vue.js·vue
晓得迷路了36 分钟前
10 分钟开发一个 Chrome 插件?Trae 让你轻松实现!
前端·javascript·trae