Lodash 源码阅读-baseFindIndex
一、介绍
baseFindIndex
是 Lodash 中一个重要的内部工具函数,它的主要作用是在数组中查找满足特定条件的元素的索引。这个函数非常灵活,因为它接受一个判断函数(predicate)作为参数,可以根据不同的条件来查找元素。它是 Lodash 中很多数组方法的基础,比如 findIndex
、findLastIndex
、indexOf
(查找 NaN 值时)等。
二、源码实现
js
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;
}
三、实现原理
1. 整体思路
说人话就是:这个函数就像是一个数组探索器,可以从左往右或者从右往左遍历数组,每到一个位置就问一下"这个是我要找的吗?"(通过 predicate 函数判断)。如果是,就说"找到了,在这个位置";如果找完了都没找到,就说"没找到,返回 -1"。
具体来说:
- 可以指定从哪个位置开始找(fromIndex)
- 可以指定是从左往右找还是从右往左找(fromRight)
- 可以自定义判断条件(predicate)
- 找到就返回位置,找不到返回 -1
2. 参数说明
array
:要搜索的数组predicate
:判断函数,用来判断是否是要找的元素fromIndex
:从哪个位置开始找fromRight
:是否从右边开始找(true 表示从右往左,false 表示从左往右)
3. 逐行解析
js
var length = array.length,
index = fromIndex + (fromRight ? 1 : -1);
- 先记住数组的长度
- 设置初始索引位置:
- 如果是从右往左找(fromRight 为 true),则初始索引为 fromIndex + 1
- 如果是从左往右找(fromRight 为 false),则初始索引为 fromIndex - 1
- 这样设置是因为在循环开始时会先执行 index-- 或 ++index
js
while (fromRight ? index-- : ++index < length) {
- 这是一个巧妙的循环条件,根据 fromRight 参数决定遍历方向:
- 如果 fromRight 为 true,则条件是 index--,从右往左遍历
- 如果 fromRight 为 false,则条件是 ++index < length,从左往右遍历
- 使用前置递增/递减运算符确保在第一次比较前就更新索引
js
if (predicate(array[index], index, array)) {
return index;
}
- 对每个元素调用 predicate 函数进行判断
- predicate 函数接收三个参数:当前元素、索引和整个数组
- 如果 predicate 返回真值,表示找到了匹配的元素,立即返回当前索引
js
return -1;
- 如果循环结束都没有找到匹配的元素,则返回 -1
四、使用示例
虽然 baseFindIndex
是内部函数,不直接暴露给用户,但我们可以通过 Lodash 的公共函数看到它的作用:
js
// 1. 通过 _.findIndex 使用
_.findIndex([1, 2, 3, 4], function (n) {
return n % 2 === 0;
}); // => 1 (找到第一个偶数的索引)
// 2. 通过 _.findLastIndex 使用
_.findLastIndex([1, 2, 3, 4], function (n) {
return n % 2 === 0;
}); // => 3 (找到最后一个偶数的索引)
// 3. 通过 _.indexOf 查找 NaN 值时使用
_.indexOf([1, NaN, 3], NaN); // => 1 (内部使用 baseFindIndex 和 baseIsNaN)
// 4. 指定起始位置
_.findIndex(
[1, 2, 3, 4, 5],
function (n) {
return n > 3;
},
3
); // => 3 (从索引3开始查找大于3的元素)
// 5. 从右向左查找
// 内部实现中,findLastIndex 会调用 baseFindIndex 并设置 fromRight 为 true
_.findLastIndex([1, 2, 3, 2, 1], function (n) {
return n === 2;
}); // => 3 (从右往左找到的第一个值为2的元素索引)
五、总结
-
方向控制:
jswhile (fromRight ? index-- : ++index < length)
通过三元运算符巧妙地控制遍历方向,一个函数同时支持从左往右和从右往左查找
-
索引初始化:
jsindex = fromIndex + (fromRight ? 1 : -1);
根据遍历方向预先调整索引,确保循环开始时能正确处理第一个元素
-
参数传递:
jspredicate(array[index], index, array);
传递完整的上下文信息给 predicate 函数,增强灵活性
-
早期返回:
jsif (predicate(array[index], index, array)) { return index; }
找到匹配项立即返回,避免不必要的遍历