Lodash源码阅读-basePullAll

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

  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);
}

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

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

  2. 性能优化:

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

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

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

相关推荐
miss14 分钟前
AI 改完不敢上线?OpenSpec 规约驱动开发实战
前端·代码规范·前端工程化
AiMan1 小时前
腾讯ima还是不可以导出左侧所有对话,只能单条导出,方法不对吗
前端
去伪存真1 小时前
并发量一高就装死?Agent 全链路“防猝死”手册
前端·agent
Moment1 小时前
AI 编程时代,聊聊我的 Mac mini 双屏桌面
前端·后端
AiMan1 小时前
先确认能不能一次把Microsofat Copilot所有内容打包一个文件导出
前端
总要学点什么1 小时前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模
上海懒图科技2 小时前
工厂厂区电子地图定位导航:厂区人车精准定位,安全合规双保障
前端·arcgis·vue
西安小哥2 小时前
端侧跑大模型,真正的门槛不在参数量:一次 596 秒到 10 秒的压缩路径复盘
前端·人工智能
陈随易2 小时前
Cloudflare 出钱,Vite 团队挑人,44 个维护者分 30 万美元
前端·后端·程序员
恋猫de小郭3 小时前
Flutter THLC 的 WebView 滚动终于从 70 FPS 提升到 120 FPS
android·前端·flutter