Lodash源码阅读-basePullAll

Lodash 源码阅读-basePullAll

概述

basePullAll 是 Lodash 中的一个内部基础函数,它实现了从数组中删除指定元素的核心逻辑。这个函数支持普通值比较、自定义迭代器和自定义比较器,是多个公共 API(如 pullAllpullAllBypullAllWith)的底层实现。它会直接修改原数组,移除所有与目标值匹配的元素。

前置学习

依赖函数

  • 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 函数的核心思路是:

  1. 遍历需要删除的值数组(values)
  2. 对于每个值,在原数组中查找匹配项(可以用迭代器或比较器自定义匹配逻辑)
  3. 找到匹配项就从原数组中删除
  4. 继续查找并删除所有匹配项,直到数组中不再有匹配的元素
  5. 最后返回修改后的原数组

这个函数不仅支持基本值的比较,还支持通过迭代器转换元素或使用自定义比较器进行复杂对象的比较,因此具有很高的灵活性。

源码解析

js 复制代码
var indexOf = comparator ? baseIndexOfWith : baseIndexOf,

智能选择查找函数:如果提供了自定义比较器,就使用 baseIndexOfWith;否则使用普通的 baseIndexOf。这种设计让函数既高效又灵活。

js 复制代码
(index = -1), (length = values.length), (seen = array);

初始化变量:设置索引起始值、缓存目标数组长度、定义 seen 变量(最初指向原数组)。

js 复制代码
if (array === values) {
  values = copyArray(values);
}

自引用检测:如果 valuesarray 是同一个数组,就创建 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 中数组元素删除操作的核心实现,体现了几个重要的编程思想:

  1. 函数灵活性:通过参数控制行为,支持多种匹配策略(直接比较、迭代器转换、自定义比较器)

  2. 性能优化

    • 智能选择查找算法
    • 处理自引用情况
    • 使用 splice 直接修改数组而非创建新数组
  3. 代码复用:作为内部实现,被多个公共 API 共享使用,遵循 DRY 原则(Don't Repeat Yourself)

  4. 边界情况处理:考虑了特殊场景如自引用数组

这个函数展示了如何设计一个既高效又灵活的数组操作工具,通过组合使用多个小型专用函数来实现复杂功能,是函数式编程思想在实际应用中的典型案例。

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼8 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax