Lodash 源码阅读-pullAllBy
概述
pullAllBy
是 Lodash 中的一个数组操作函数,用于根据指定的迭代器函数比较标准,从数组中移除特定元素。与 pullAll
不同的是,它允许通过自定义迭代器来决定如何比较元素,使其能够处理更复杂的数据结构。该函数会直接修改原数组(具有副作用),并返回修改后的数组。
前置学习
依赖函数
- basePullAll:内部核心函数,实现数组元素移除的底层逻辑
- getIteratee:获取适当的迭代器函数,支持多种输入格式(函数、字符串、对象等)
技术知识
- 数组操作:数组的修改和引用
- 高阶函数:函数作为参数传递
- 迭代器模式:使用迭代器函数处理集合元素
- 函数参数校验:参数有效性检查
源码实现
js
function pullAllBy(array, values, iteratee) {
return array && array.length && values && values.length
? basePullAll(array, values, getIteratee(iteratee, 2))
: array;
}
实现思路
pullAllBy
的实现非常简洁明了。它首先检查源数组和目标值数组是否都存在且非空,然后调用 basePullAll
函数执行实际的移除操作,同时传入由 getIteratee
处理后的迭代器函数。这种设计将函数主体的复杂性委托给专门的内部函数,使公共 API 保持简洁。
核心思路是:
- 验证输入数组的有效性
- 处理迭代器参数,使其标准化
- 调用内部函数完成实际工作
- 返回修改后的原数组
源码解析
函数签名:
js
function pullAllBy(array, values, iteratee)
array
:要修改的原始数组values
:包含要移除元素的数组iteratee
:迭代器函数,用于生成比较标准
函数实现细节:
js
return array && array.length && values && values.length
? basePullAll(array, values, getIteratee(iteratee, 2))
: array;
这行代码包含了几个关键逻辑:
-
输入验证 :
array && array.length && values && values.length
- 检查
array
和values
是否都是有效的非空数组 - 如果任一条件不满足,直接返回原数组,避免不必要的处理
- 检查
-
迭代器处理 :
getIteratee(iteratee, 2)
- 使用
getIteratee
将用户提供的iteratee
转换为标准函数 - 数字
2
表示迭代器函数的参数数量(arity) - 这允许用户传入不同类型的迭代器,如字符串属性名、函数等
- 使用
-
核心调用 :
basePullAll(array, values, ...)
- 调用内部函数
basePullAll
执行实际的元素移除操作 - 将处理后的迭代器作为第三个参数传入
- 调用内部函数
pullAllBy
函数的关键特性是它支持多种形式的迭代器参数:
- 函数:直接使用用户提供的函数
- 字符串:将其视为属性路径,创建一个提取该属性的函数
- 对象:创建一个检查属性匹配的函数
这种灵活性使 pullAllBy
能够适应各种使用场景。
应用场景
- 对象数组过滤:根据对象的特定属性移除元素
js
// 从用户列表中移除指定ID的用户
let users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Charlie" },
];
_.pullAllBy(users, [{ id: 1 }, { id: 3 }], "id");
// users 现在是 [{ id: 2, name: "Bob" }]
- 基于转换后的值比较:通过迭代器转换元素后再比较
js
// 忽略小数部分比较数字
let numbers = [1.2, 2.3, 3.7, 4.5, 5.1];
_.pullAllBy(numbers, [1.8, 3.2, 5.9], Math.floor);
// numbers 现在是 [2.3, 4.5]
// 因为Math.floor(1.2)===Math.floor(1.8),Math.floor(3.7)===Math.floor(3.2),Math.floor(5.1)===Math.floor(5.9)
js
// 根据数字的十位数移除元素
let scores = [22, 35, 47, 58, 73, 86];
_.pullAllBy(scores, [31, 42, 79], (num) => Math.floor(num / 10));
// scores 现在是 [47, 58, 86]
// 22和31、35和42、73和79的十位数相同,所以被移除
- 数据清洗:移除具有特定特征的元素
js
// 从产品列表中移除所有停产的产品
let products = [
{ name: "Laptop", status: "active" },
{ name: "Phone", status: "discontinued" },
{ name: "Tablet", status: "active" },
{ name: "Camera", status: "discontinued" },
];
_.pullAllBy(products, [{ status: "discontinued" }], "status");
// products 现在只包含状态为active的产品
总结
pullAllBy
函数展示了 Lodash 库中常见的几个设计原则:
- 委托模式:将复杂逻辑委托给专门的内部函数,保持公共 API 的简洁
- 参数灵活性:支持多种类型的迭代器参数,提高函数的适用性
- 输入验证:在执行操作前验证参数的有效性,增强代码健壮性
- 一致返回:不论是否执行操作,始终返回原数组,维持接口一致性
- 副作用明确:函数文档明确说明该函数会修改原数组
pullAllBy
虽然实现简单,但通过与其他内部函数(如 basePullAll
和 getIteratee
)的组合,提供了强大的功能。它允许用户以灵活的方式指定元素比较标准,使得从数组中移除元素的操作既直观又强大。这种"简单接口,复杂实现"的设计思想,使得 Lodash 库易于使用但功能强大。