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
- 默认从数组的最后一个元素开始查找(索引为
length - 1
) - 如果指定了起始位置
fromIndex
:- 将它转成整数
- 如果是负数(如 -2 表示倒数第二个位置),就从数组末尾倒数
- 确保计算后的位置不会超出数组范围
- 调用
baseFindIndex
函数执行实际查找,传入true
表示从右向左查找
这种设计把参数处理和核心查找逻辑分开,代码结构更清晰。
源码解析
参数检查
javascript
var length = array == null ? 0 : array.length;
if (!length) {
return -1;
}
这段代码先检查输入的数组:
- 如果是
null
或undefined
(用==
可以同时检查这两种情况),就把长度设为 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);
}
这段代码处理查找的起始位置:
- 默认从最后一个元素开始查找(
length - 1
) - 如果指定了
fromIndex
:- 用
toInteger
把它转成整数(处理类似 "3" 这样的字符串) - 如果是负数,就从末尾向前计算:
-1
表示最后一个元素-2
表示倒数第二个元素- 用
nativeMax(length + index, 0)
确保不会小于 0
- 如果是正数,就确保不超过最大索引:
- 用
nativeMin(index, length - 1)
限制上界
- 用
- 用
这种处理方式使得函数更加健壮,能处理各种奇怪的输入情况。
核心查找
javascript
return baseFindIndex(array, getIteratee(predicate, 3), index, true);
最后调用 baseFindIndex
函数执行实际查找:
- 要查找的数组
array
- 处理后的条件函数
getIteratee(predicate, 3)
getIteratee
会根据predicate
的类型返回合适的函数- 数字
3
表示这个函数会接收三个参数:当前元素、索引和原数组
- 起始位置
index
- 最后的
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;
}
这个函数通过循环遍历数组,对每个元素应用条件函数:
- 如果
fromRight
为true
(即findLastIndex
的情况),就从右向左遍历 - 如果找到符合条件的元素,立即返回索引
- 如果遍历完都没找到,返回 -1
这种设计让 baseFindIndex
可以同时被 findIndex
和 findLastIndex
使用,减少代码重复。
总结
findLastIndex
函数虽然简单,但却很实用,它巧妙地解决了"从后向前查找"这个常见需求。通过分析源码,我们可以学到几点:
-
灵活的 API 设计
- 支持多种形式的查找条件,适应不同场景
- 可选的起始位置参数,增加灵活性
- 负数索引的支持,符合直觉的使用方式
-
健壮的参数处理
- 对空数组和非数组输入的处理
- 对越界索引的修正
- 参数类型转换,容错性强
-
代码复用的艺术
- 与
findIndex
共用同一个基础实现 - 通过参数控制查找方向,避免逻辑重复
- 与
-
性能优化考虑
- 空数组短路处理,避免无效计算
- 找到结果立即返回,不继续遍历
这些设计理念不仅适用于这个函数,也适用于我们日常编写的代码。findLastIndex
是 Lodash 众多实用函数中的一个,它弥补了 JavaScript 标准库的不足,让数组操作更加便捷和强大。