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 标准库的不足,让数组操作更加便捷和强大。

相关推荐
JiangJiang5 分钟前
🚀 Vue人看React useRef:它不只是替代 ref
javascript·react.js·面试
1024小神10 分钟前
在GitHub action中使用添加项目中配置文件的值为环境变量
前端·javascript
龙骑utr14 分钟前
qiankun微应用动态设置静态资源访问路径
javascript
Jasmin Tin Wei15 分钟前
css易混淆的知识点
开发语言·javascript·ecmascript
齐尹秦18 分钟前
CSS 列表样式学习笔记
前端
wsz777722 分钟前
js封装系列(一)
javascript
Mnxj22 分钟前
渐变边框设计
前端
用户76787977373225 分钟前
由Umi升级到Next方案
前端·next.js
快乐的小前端26 分钟前
TypeScript基础一
前端
北凉温华27 分钟前
UniApp项目中的多服务环境配置与跨域代理实现
前端