Lodash 源码阅读-pullAllWith
概述
pullAllWith
是 Lodash 库中的一个数组操作函数,用于从数组中移除指定的元素。它的特别之处在于可以接收一个自定义的比较器(comparator)函数,通过这个函数来决定哪些元素应该被移除。与其他数组差异函数不同,pullAllWith
会直接修改原数组,是一个具有副作用的函数。
前置学习
依赖函数
- basePullAll:核心实现函数,负责实际的数组元素移除操作
- baseIndexOfWith:支持自定义比较器的数组元素查找函数
- baseIndexOf:基础的数组索引查找函数
技术知识
- 数组操作:数组元素的删除和查找
- 高阶函数:函数作为参数传递
- 比较器模式:通过自定义比较逻辑来处理复杂对象的比较
- 函数式编程:不可变性与副作用
源码实现
js
function pullAllWith(array, values, comparator) {
return array && array.length && values && values.length
? basePullAll(array, values, undefined, comparator)
: array;
}
实现思路
pullAllWith
的实现非常简洁。函数首先检查输入数组和目标值数组是否都存在且非空,如果条件满足,则调用内部的 basePullAll
函数执行实际的移除操作,其中第三个参数(迭代器)传入 undefined
,第四个参数传入自定义比较器。这种设计将核心逻辑委托给专门的内部函数,保持了公共 API 的简洁性和易用性。
源码解析
函数签名:
js
function pullAllWith(array, values, comparator)
array
:要修改的数组values
:包含要移除元素的数组comparator
:自定义比较器函数,接收两个参数(arrVal, othVal),用于比较数组元素
函数主体:
js
return array && array.length && values && values.length
? basePullAll(array, values, undefined, comparator)
: array;
这行代码包含以下关键部分:
-
参数校验 :
array && array.length && values && values.length
这是一个简洁的条件表达式,用于检查:
array
存在且有长度(不是空数组)values
存在且有长度(不是空数组)
这种检查方式可以避免在无需处理的情况下调用复杂的内部函数,提高了效率。
-
调用内部实现 :
basePullAll(array, values, undefined, comparator)
当条件满足时,调用
basePullAll
函数进行实际操作:- 第一个参数
array
是要修改的数组 - 第二个参数
values
是包含要移除元素的数组 - 第三个参数传入
undefined
,表示不使用迭代器 - 第四个参数传入用户提供的
comparator
比较器函数
- 第一个参数
-
返回修改后的数组:
- 如果条件不满足,直接返回原始
array
- 如果条件满足,返回
basePullAll
处理后的array
(由于是直接修改,实际上返回的就是原数组的引用)
- 如果条件不满足,直接返回原始
basePullAll
的核心实现:
当 pullAllWith
调用 basePullAll
时,内部会根据是否提供了比较器来选择不同的查找方法:
js
var indexOf = comparator ? baseIndexOfWith : baseIndexOf;
如果提供了比较器,就使用 baseIndexOfWith
函数,它会在每次比较元素时调用自定义比较器。这使得 pullAllWith
能够支持复杂对象的比较,而不仅限于简单值的严格相等比较。
使用示例
js
// 使用 _.isEqual 作为比较器移除对象
var array = [
{ x: 1, y: 2 },
{ x: 3, y: 4 },
{ x: 5, y: 6 },
];
_.pullAllWith(array, [{ x: 3, y: 4 }], _.isEqual);
console.log(array);
// => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
// 自定义比较器 - 只比较x属性
var array = [
{ x: 1, y: 2 },
{ x: 3, y: 4 },
{ x: 5, y: 6 },
];
_.pullAllWith(array, [{ x: 3 }], (arrVal, othVal) => arrVal.x === othVal.x);
console.log(array);
// => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }]
// 在不存在的情况下不会修改数组
var emptyArray = [];
_.pullAllWith(emptyArray, [1, 2, 3], _.isEqual);
console.log(emptyArray);
// => []
// 如果values为空,也不会修改数组
var array = [1, 2, 3];
_.pullAllWith(array, [], _.isEqual);
console.log(array);
// => [1, 2, 3]
总结
-
灵活的比较能力 :
pullAllWith
通过支持自定义比较器,极大地提升了数组操作的灵活性,能够应对复杂对象的比较场景。 -
副作用设计 :与许多 Lodash 函数不同,
pullAllWith
直接修改原数组,这种设计在某些场景下可以提高性能,但使用时需要注意它会改变输入数组。 -
优雅的参数校验:函数使用简洁的条件表达式进行参数校验,避免了冗长的 if 语句,同时保证了代码的健壮性。
-
分层设计 :将核心逻辑委托给专门的内部函数
basePullAll
,体现了良好的代码分层和职责分离,使公共 API 保持简洁明了。