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

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

相关推荐
小声读源码20 分钟前
【技巧】dify前端源代码修改第一弹-增加tab页
前端·pnpm·next.js·dify
假客套30 分钟前
2025 后端自学UNIAPP【项目实战:旅游项目】7、景点详情页面【完结】
前端·uni-app·旅游
程序员小张丶42 分钟前
基于React Native开发HarmonyOS 5.0主题应用技术方案
javascript·react native·react.js·主题·harmonyos5.0
Captaincc44 分钟前
Ilya 现身多大毕业演讲:AI 会完成我们能做的一切
前端·ai编程
teeeeeeemo1 小时前
Vue数据响应式原理解析
前端·javascript·vue.js·笔记·前端框架·vue
Sahas10191 小时前
__VUE_PROD_HYDRATION_MISMATCH_DETAILS__ is not explicitly defined.
前端·javascript·vue.js
Jinxiansen02111 小时前
Vue 3 实战:【加强版】公司通知推送(WebSocket + token 校验 + 心跳机制)
前端·javascript·vue.js·websocket·typescript
MrSkye1 小时前
React入门:组件化思想?数据驱动?
前端·react.js·面试
BillKu1 小时前
Java解析前端传来的Unix时间戳
java·前端·unix
@Mr_LiuYang1 小时前
网页版便签应用开发:HTML5本地存储与拖拽交互实践
前端·交互·html5·html5便签应用