Lodash源码阅读-findLastIndex

Lodash 源码阅读-findLastIndex

概述

findLastIndex 是 Lodash 库中很实用的一个数组查找函数,它可以从数组的右侧(末尾)开始查找符合条件的元素,并返回这个元素的索引位置。如果没找到符合条件的元素,就返回 -1。简单来说,它就是 findIndex 函数的反向版本,查找方向是从后往前。

与 JavaScript 原生的数组方法相比,findLastIndex 提供了更灵活的查找功能:

  • 支持多种形式的查找条件(函数、对象、数组、字符串)
  • 可以指定从哪个位置开始查找
  • 处理边界情况更加全面

前置学习

依赖函数

findLastIndex 依赖以下几个函数才能正常工作:

  • baseFindIndex:核心查找逻辑的函数,支持从左到右或从右到左查找
  • toInteger:将任意值转换为整数的函数
  • getIteratee:根据不同类型的输入,返回对应的迭代器函数
  • baseIteratee:创建标准迭代器函数的基础实现
  • nativeMax :就是原生的 Math.max 函数
  • nativeMin :就是原生的 Math.min 函数

技术知识

要理解 findLastIndex,需要掌握这些概念:

  • 高阶函数:函数作为参数传递给另一个函数
  • 迭代器模式:使用统一方式处理不同形式的查找条件
  • 短路优化:找到结果后立即返回,不再继续查找
  • 参数校验和默认值:处理各种可能的输入情况
  • 边界情况处理:处理特殊情况,如空数组、负数索引等

源码实现

javascript 复制代码
function findLastIndex(array, predicate, fromIndex) {
  var length = array == null ? 0 : array.length;
  if (!length) {
    return -1;
  }
  var index = length - 1;
  if (fromIndex !== undefined) {
    index = toInteger(fromIndex);
    index =
      fromIndex < 0
        ? nativeMax(length + index, 0)
        : nativeMin(index, length - 1);
  }
  return baseFindIndex(array, getIteratee(predicate, 3), index, true);
}

实现思路

findLastIndex 的实现逻辑很直观:

  1. 首先检查数组是否为空(或不是数组),如果是则直接返回 -1
  2. 默认从数组的最后一个元素开始查找(索引为 length - 1
  3. 如果指定了起始位置 fromIndex
    • 将它转成整数
    • 如果是负数(如 -2 表示倒数第二个位置),就从数组末尾倒数
    • 确保计算后的位置不会超出数组范围
  4. 调用 baseFindIndex 函数执行实际查找,传入 true 表示从右向左查找

这种设计把参数处理和核心查找逻辑分开,代码结构更清晰。

源码解析

参数检查

javascript 复制代码
var length = array == null ? 0 : array.length;
if (!length) {
  return -1;
}

这段代码先检查输入的数组:

  • 如果是 nullundefined(用 == 可以同时检查这两种情况),就把长度设为 0
  • 如果长度为 0(空数组),直接返回 -1 表示没找到
  • 这是个常见的短路优化,避免对空数组做无谓的处理

起始位置计算

javascript 复制代码
var index = length - 1;
if (fromIndex !== undefined) {
  index = toInteger(fromIndex);
  index =
    fromIndex < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);
}

这段代码处理查找的起始位置:

  1. 默认从最后一个元素开始查找(length - 1
  2. 如果指定了 fromIndex
    • toInteger 把它转成整数(处理类似 "3" 这样的字符串)
    • 如果是负数,就从末尾向前计算:
      • -1 表示最后一个元素
      • -2 表示倒数第二个元素
      • nativeMax(length + index, 0) 确保不会小于 0
    • 如果是正数,就确保不超过最大索引:
      • nativeMin(index, length - 1) 限制上界

这种处理方式使得函数更加健壮,能处理各种奇怪的输入情况。

核心查找

javascript 复制代码
return baseFindIndex(array, getIteratee(predicate, 3), index, true);

最后调用 baseFindIndex 函数执行实际查找:

  1. 要查找的数组 array
  2. 处理后的条件函数 getIteratee(predicate, 3)
    • getIteratee 会根据 predicate 的类型返回合适的函数
    • 数字 3 表示这个函数会接收三个参数:当前元素、索引和原数组
  3. 起始位置 index
  4. 最后的 true 表示从右向左查找(这是与 findIndex 的关键区别)

getIteratee 是 Lodash 的一个核心工具,它让 findLastIndex 支持多种形式的条件:

  • 函数:直接使用该函数
  • 对象:创建一个检查对象属性的函数,如 {id: 1} 会转为 obj => obj.id === 1
  • 数组:创建一个检查对象属性值的函数,如 ['name', 'Tom'] 会转为 obj => obj.name === 'Tom'
  • 字符串:创建一个获取对象属性的函数,如 'active' 会转为 obj => !!obj.active

这种灵活性让函数更容易使用,适应更多场景。

baseFindIndex 的工作原理

baseFindIndex 是实际执行查找的函数:

javascript 复制代码
function baseFindIndex(array, predicate, fromIndex, fromRight) {
  var length = array.length,
    index = fromIndex + (fromRight ? 1 : -1);

  while (fromRight ? index-- : ++index < length) {
    if (predicate(array[index], index, array)) {
      return index;
    }
  }
  return -1;
}

这个函数通过循环遍历数组,对每个元素应用条件函数:

  • 如果 fromRighttrue(即 findLastIndex 的情况),就从右向左遍历
  • 如果找到符合条件的元素,立即返回索引
  • 如果遍历完都没找到,返回 -1

这种设计让 baseFindIndex 可以同时被 findIndexfindLastIndex 使用,减少代码重复。

总结

findLastIndex 函数虽然简单,但却很实用,它巧妙地解决了"从后向前查找"这个常见需求。通过分析源码,我们可以学到几点:

  1. 灵活的 API 设计

    • 支持多种形式的查找条件,适应不同场景
    • 可选的起始位置参数,增加灵活性
    • 负数索引的支持,符合直觉的使用方式
  2. 健壮的参数处理

    • 对空数组和非数组输入的处理
    • 对越界索引的修正
    • 参数类型转换,容错性强
  3. 代码复用的艺术

    • findIndex 共用同一个基础实现
    • 通过参数控制查找方向,避免逻辑重复
  4. 性能优化考虑

    • 空数组短路处理,避免无效计算
    • 找到结果立即返回,不继续遍历

这些设计理念不仅适用于这个函数,也适用于我们日常编写的代码。findLastIndex 是 Lodash 众多实用函数中的一个,它弥补了 JavaScript 标准库的不足,让数组操作更加便捷和强大。

相关推荐
apcipot_rain3 小时前
【应用密码学】实验五 公钥密码2——ECC
前端·数据库·python
油丶酸萝卜别吃3 小时前
OpenLayers 精确经过三个点的曲线绘制
javascript
ShallowLin3 小时前
vue3学习——组合式 API:生命周期钩子
前端·javascript·vue.js
Nejosi_念旧3 小时前
Vue API 、element-plus自动导入插件
前端·javascript·vue.js
互联网搬砖老肖3 小时前
Web 架构之攻击应急方案
前端·架构
pixle04 小时前
Vue3 Echarts 3D饼图(3D环形图)实现讲解附带源码
前端·3d·echarts
麻芝汤圆4 小时前
MapReduce 入门实战:WordCount 程序
大数据·前端·javascript·ajax·spark·mapreduce
juruiyuan1116 小时前
FFmpeg3.4 libavcodec协议框架增加新的decode协议
前端
Peter 谭6 小时前
React Hooks 实现原理深度解析:从基础到源码级理解
前端·javascript·react.js·前端框架·ecmascript
周胡杰7 小时前
鸿蒙接入flutter环境变量配置windows-命令行或者手动配置-到项目的创建-运行demo项目
javascript·windows·flutter·华为·harmonyos·鸿蒙·鸿蒙系统