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. 边界情况处理:考虑了特殊场景如自引用数组

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

相关推荐
wangruofeng8 小时前
2026 Web 技术选型指南,AI 时代如何选择适合项目的技术栈
前端·aigc·ai编程
honkun68 小时前
vue 表格组件 vxe-table 修改滚动条样式,修改滚动条样式,统一宽度、颜色与自动隐藏
前端·javascript·vue.js
爱勇宝9 小时前
研究数字孪生的这半年,做了个 Demo,也有了一些新想法
前端·程序员
不可能片场9 小时前
工程实践 一个缓存把选片从十分钟压到一分钟
前端·electron
前端小万9 小时前
探索副业五个月,最终以放弃告终
前端·后端·面试
FRing039 小时前
JavaScript入门到精通二十四(正则表达式、元字符、修饰符)
开发语言·前端·javascript·正则表达式
葡萄城技术团队10 小时前
在 SpreadJS 里新增右键菜单,把业务动作放到用户手边
前端
大明二代10 小时前
从 RAG 到有记忆的 Agent:Mastra Memory 与 PostgreSQL 实战
前端·javascript·人工智能
deli00710 小时前
代码截图一键出 PNG 实测:3 种窗口风格,长行超 95 字符会被裁
前端
大哥430910 小时前
慢 ≠ 挂:AI 搜索降级判定的设计,与一次 reasoning 模型排查复盘
前端