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

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

相关推荐
augenstern41619 分钟前
webpack重构优化
前端·webpack·重构
海拥✘23 分钟前
CodeBuddy终极测评:中国版Cursor的开发革命(含安装指南+HTML游戏实战)
前端·游戏·html
寧笙(Lycode)1 小时前
React系列——HOC高阶组件的封装与使用
前端·react.js·前端框架
asqq81 小时前
CSS 中的 ::before 和 ::after 伪元素
前端·css
拖孩1 小时前
【Nova UI】十五、打造组件库之滚动条组件(上):滚动条组件的起步与进阶
前端·javascript·css·vue.js·ui组件库
苹果电脑的鑫鑫1 小时前
element中表格文字剧中可以使用的属性
javascript·vue.js·elementui
Hejjon2 小时前
Vue2 elementUI 二次封装命令式表单弹框组件
前端·vue.js
一丝晨光2 小时前
数值溢出保护?数值溢出应该是多少?Swift如何让整数计算溢出不抛出异常?类型最大值和最小值?
java·javascript·c++·rust·go·c·swift
小堃学编程2 小时前
前端学习(3)—— CSS实现热搜榜
前端·学习
Wannaer2 小时前
从 Vue3 回望 Vue2:响应式的内核革命
前端·javascript·vue.js