Lodash源码阅读-uniqBy

Lodash 源码阅读-uniqBy

概述

uniqBy 函数用于创建一个数组的去重版本,通过指定的迭代器函数为数组的每个元素生成用于比较的标准。它只保留数组中每个元素(基于迭代器返回值)的第一次出现。结果数组中元素的顺序取决于它们在原数组中首次出现的位置。

前置学习

依赖函数

  • baseUniq:内部函数,实现数组去重的核心逻辑
  • getIteratee:获取适当的迭代器函数

技术知识

  • 数组去重算法
  • 高阶函数:接收函数作为参数
  • 迭代器函数:为每个数组元素生成比较标准
  • JavaScript 中的 SameValueZero 比较
  • 函数柯里化和函数式编程

源码实现

javascript 复制代码
function uniqBy(array, iteratee) {
  return array && array.length ? baseUniq(array, getIteratee(iteratee, 2)) : [];
}

实现思路

uniqBy 函数的实现非常简洁,但功能强大。它首先检查输入数组是否存在且有长度,如果是,则调用内部的 baseUniq 函数进行实际的去重操作;否则返回空数组。

在调用 baseUniq 时,它传入了两个参数:

  1. 原始数组 array
  2. 通过 getIteratee(iteratee, 2) 获取的迭代器函数,其中 2 表示迭代器函数应接收一个参数(数组元素)

uniqBy 的核心在于允许用户自定义如何生成比较元素唯一性的标准,这使得去重过程更加灵活和强大。

源码解析

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

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

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

  • array:要去重的数组
  • iteratee:迭代器函数,为每个元素生成比较的键值
javascript 复制代码
return array && array.length ? baseUniq(array, getIteratee(iteratee, 2)) : [];

这行代码完成了主要工作:

  1. array && array.length 检查数组是否存在且有长度
  2. 如果条件为真,调用 baseUniq 函数
    • 第一个参数是原始数组 array
    • 第二个参数是通过 getIteratee(iteratee, 2) 获取的迭代器函数
  3. 如果条件为假(数组不存在或为空),返回空数组 []

getIteratee(iteratee, 2) 是个关键部分,它根据传入的 iteratee 类型返回一个适当的函数:

  • 如果 iteratee 是函数,它会被直接使用
  • 如果 iteratee 是字符串,它会被转换为属性访问函数(如 _.property 的效果)
  • 如果 iteratee 是对象,它会被转换为匹配器函数(如 _.matches 的效果)
  • 如果 iteratee 是数组,它会被转换为属性-值匹配函数(如 _.matchesProperty 的效果)
  • 如果 iteratee 为 null 或 undefined,会返回 identity 函数(返回原值)

参数 2 表示返回的迭代器函数的参数数量 (arity),这里指定为接收一个参数。

baseUniq 执行时,它会使用这个迭代器函数为每个数组元素计算一个 "键",然后使用这些键来确定元素是否唯一。相同键的元素中,只有第一个会被保留在结果数组中。

总结

uniqBy 函数是 Lodash 的一个实用工具,通过允许自定义比较标准,它提供了比简单的 uniq 函数更强大的数组去重能力。

这个函数的主要特点包括:

  1. 灵活性:支持多种形式的迭代器(函数、字符串路径、对象匹配器等)
  2. 保留原始值:虽然比较是基于迭代器返回的值,但结果数组中保留的是原始元素
  3. 保持顺序:保留了元素在原数组中的相对顺序(首次出现的位置)
  4. 参数验证:优雅地处理边缘情况,如空数组或 null 值

在实际应用中,uniqBy 特别适用于:

  • 去除具有相同特定属性值的对象
  • 基于复杂计算进行去重
  • 实现类似 SQL 中的 "GROUP BY" 功能(取每组第一个元素)

从设计模式角度看,uniqBy 使用了策略模式(允许用户选择不同的去重策略)和委托模式(将具体实现委托给内部函数)。这种设计让 API 保持简洁易用,同时内部实现可以更加复杂和高效。

相关推荐
GanGuaGua12 分钟前
Vue3:脚手架
前端·javascript·css·vue.js·vue
weixin_4316004434 分钟前
使用 Vue Tour 封装一个统一的页面引导组件
javascript·vue.js·ecmascript
鸡吃丸子1 小时前
常见的实时通信技术(轮询、sse、websocket、webhooks)
前端·websocket·状态模式
胡斌附体2 小时前
vue添加loading后修复页面渲染问题
前端·javascript·vue.js·渲染·v-if·异步加载
Dontla2 小时前
Webpack DefinePlugin插件介绍(允许在编译时创建JS全局常量,常量可以在源代码中直接使用)JS环境变量
运维·javascript·webpack
酷爱码2 小时前
css中的 vertical-align与line-height作用详解
前端·css
沐土Arvin3 小时前
深入理解 requestIdleCallback:浏览器空闲时段的性能优化利器
开发语言·前端·javascript·设计模式·html
专注VB编程开发20年3 小时前
VB.NET关于接口实现与简化设计的分析,封装其他类
java·前端·数据库
小妖6663 小时前
css 中 content: “\e6d0“ 怎么变成图标的?
前端·css
L耀早睡4 小时前
mapreduce打包运行
大数据·前端·spark·mapreduce