Lodash 源码阅读-basePullAll
概述
basePullAll
是 Lodash 中的一个内部基础函数,它实现了从数组中删除指定元素的核心逻辑。这个函数支持普通值比较、自定义迭代器和自定义比较器,是多个公共 API(如 pullAll
、pullAllBy
和 pullAllWith
)的底层实现。它会直接修改原数组,移除所有与目标值匹配的元素。
前置学习
依赖函数
- baseIndexOf:在数组中查找元素的基础函数
- baseIndexOfWith:支持自定义比较器的元素查找函数
- arrayMap:对数组元素进行映射转换
- baseUnary:创建一元函数的工具
- copyArray:数组浅拷贝函数
技术知识
- 数组操作:数组元素的删除、查找
- 闭包:函数作为参数和返回值
- Array.prototype.splice:原生数组元素删除方法
- 引用类型比较:对象间的相等比较策略
源码实现
js
function basePullAll(array, values, iteratee, comparator) {
var indexOf = comparator ? baseIndexOfWith : baseIndexOf,
index = -1,
length = values.length,
seen = array;
if (array === values) {
values = copyArray(values);
}
if (iteratee) {
seen = arrayMap(array, baseUnary(iteratee));
}
while (++index < length) {
var fromIndex = 0,
value = values[index],
computed = iteratee ? iteratee(value) : value;
while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
if (seen !== array) {
splice.call(seen, fromIndex, 1);
}
splice.call(array, fromIndex, 1);
}
}
return array;
}
实现思路
basePullAll
函数的核心思路是:
- 遍历需要删除的值数组(values)
- 对于每个值,在原数组中查找匹配项(可以用迭代器或比较器自定义匹配逻辑)
- 找到匹配项就从原数组中删除
- 继续查找并删除所有匹配项,直到数组中不再有匹配的元素
- 最后返回修改后的原数组
这个函数不仅支持基本值的比较,还支持通过迭代器转换元素或使用自定义比较器进行复杂对象的比较,因此具有很高的灵活性。
源码解析
js
var indexOf = comparator ? baseIndexOfWith : baseIndexOf,
智能选择查找函数:如果提供了自定义比较器,就使用 baseIndexOfWith
;否则使用普通的 baseIndexOf
。这种设计让函数既高效又灵活。
js
(index = -1), (length = values.length), (seen = array);
初始化变量:设置索引起始值、缓存目标数组长度、定义 seen
变量(最初指向原数组)。
js
if (array === values) {
values = copyArray(values);
}
自引用检测:如果 values
和 array
是同一个数组,就创建 values
的副本。这是为了避免在删除过程中干扰遍历。
js
if (iteratee) {
seen = arrayMap(array, baseUnary(iteratee));
}
应用迭代器:如果提供了迭代器函数,就将原数组中的每个元素通过迭代器映射,得到一个新的 seen
数组。这里使用 baseUnary
确保迭代器只接收一个参数。
js
while (++index < length) {
var fromIndex = 0,
value = values[index],
computed = iteratee ? iteratee(value) : value;
开始遍历要删除的值:对于每个要删除的值,记录起始搜索位置,获取当前值。如果有迭代器,应用迭代器处理当前值。
js
while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
查找匹配元素:使用之前选择的 indexOf
方法在 seen
数组中查找匹配项。重复查找直到找不到为止(indexOf
返回 -1)。
js
if (seen !== array) {
splice.call(seen, fromIndex, 1);
}
splice.call(array, fromIndex, 1);
删除匹配元素:如果 seen
不是原数组(说明使用了迭代器),就同时从 seen
和原数组中删除元素;否则只从原数组中删除。使用 splice
方法进行元素删除。
js
}
}
return array;
循环结束后返回修改后的原数组。
总结
basePullAll
函数是 Lodash 中数组元素删除操作的核心实现,体现了几个重要的编程思想:
-
函数灵活性:通过参数控制行为,支持多种匹配策略(直接比较、迭代器转换、自定义比较器)
-
性能优化:
- 智能选择查找算法
- 处理自引用情况
- 使用
splice
直接修改数组而非创建新数组
-
代码复用:作为内部实现,被多个公共 API 共享使用,遵循 DRY 原则(Don't Repeat Yourself)
-
边界情况处理:考虑了特殊场景如自引用数组
这个函数展示了如何设计一个既高效又灵活的数组操作工具,通过组合使用多个小型专用函数来实现复杂功能,是函数式编程思想在实际应用中的典型案例。