Lodash 源码阅读-findIndex
概述
findIndex
是 Lodash 中用来查找数组元素位置的实用函数。它的作用是在数组中找出第一个满足条件的元素的索引位置,找不到就返回 -1。它比原生的 Array.prototype.findIndex
功能更强大,支持多种查询方式,不仅可以用函数判断,还可以用对象匹配、属性值匹配等简便写法。
前置学习
依赖函数
baseFindIndex
:核心查找逻辑的实现,支持从左到右或从右到左查找toInteger
:将值转换为整数,处理 fromIndex 参数getIteratee
:获取迭代器函数,支持多种简写形式baseIteratee
:转换各种类型输入为标准的迭代器函数nativeMax
:原生 Math.max 函数的引用
技术知识
- 高阶函数:函数作为参数传递
- 参数处理:处理可选参数和默认值
- 索引计算:处理负数索引的情况
- 迭代器转换:将不同类型的参数转换为迭代器函数
- 短路优化:对空数组快速返回结果
源码实现
javascript
function findIndex(array, predicate, fromIndex) {
var length = array == null ? 0 : array.length;
if (!length) {
return -1;
}
var index = fromIndex == null ? 0 : toInteger(fromIndex);
if (index < 0) {
index = nativeMax(length + index, 0);
}
return baseFindIndex(array, getIteratee(predicate, 3), index);
}
实现思路
findIndex
函数的实现思路很直观:
- 先检查数组是否为空或不存在,是的话直接返回 -1
- 处理起始位置参数
fromIndex
:- 如果没提供,默认从 0 开始查找
- 如果提供了,转换为整数
- 如果是负数,就从数组末尾往前算,但不小于 0
- 调用
baseFindIndex
进行实际的查找,传入:- 要查找的数组
- 转换后的断言函数(通过 getIteratee 处理)
- 计算好的起始查找位置
这种设计把参数处理和核心查找逻辑分开,让代码更清晰易懂。
源码解析
参数检查和长度判断
javascript
var length = array == null ? 0 : array.length;
if (!length) {
return -1;
}
这段代码先判断数组是否存在。使用 array == null
可以同时检查 null
和 undefined
。如果数组不存在,就把长度设为 0;否则,获取数组的实际长度。
如果长度为 0(表示数组为空或不存在),直接返回 -1,表示找不到元素。这是一个常见的优化手段,避免对空数组做无谓的处理。
起始位置处理
javascript
var index = fromIndex == null ? 0 : toInteger(fromIndex);
if (index < 0) {
index = nativeMax(length + index, 0);
}
这段代码处理起始查找位置 fromIndex
:
- 如果
fromIndex
是null
或undefined
,默认使用 0 - 否则用
toInteger
把它转成整数 - 如果转换后是负数,从数组末尾向前计算:
length + index
表示从末尾向前数|index|
个位置nativeMax(length + index, 0)
确保结果不小于 0
这种设计让函数支持负数索引,比如 -1
表示从最后一个元素开始找,-2
表示从倒数第二个开始找。
核心查找逻辑
javascript
return baseFindIndex(array, getIteratee(predicate, 3), index);
最后调用 baseFindIndex
函数进行实际查找,传入三个参数:
array
:要查找的数组getIteratee(predicate, 3)
:转换后的断言函数getIteratee
会根据predicate
类型返回合适的迭代器函数- 参数
3
表示迭代器函数接收三个参数:元素值、索引和数组本身
index
:计算好的起始位置
getIteratee
是 Lodash 的核心函数,它能将各种类型的输入转换为标准函数:
- 函数类型:直接使用
- 对象类型:转为检查对象属性的函数
- 数组类型:转为检查对象属性值的函数
- 字符串类型:转为获取对象属性的函数
这使得 findIndex
能接受多种形式的参数,极大提高了使用灵活性。
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;
}
这个函数通过循环遍历数组,对每个元素调用断言函数。如果断言返回 true
,立即返回当前索引;如果遍历完整个数组都没找到符合条件的元素,返回 -1。
参数 fromRight
控制遍历方向。findIndex
不传这个参数,所以是从左向右遍历;而 findLastIndex
传入 true
,从右向左遍历。
总结
findIndex
是 Lodash 中一个简单却强大的数组工具函数,它的亮点包括:
- 灵活的参数形式:支持函数、对象、数组、字符串等多种断言形式
- 合理的参数处理:妥善处理默认值和负数索引
- 性能优化:对空数组快速返回,避免无谓计算
- 代码分层:参数处理和核心逻辑分离,职责明确
这些特点使得 findIndex
比原生的 Array.prototype.findIndex
更强大,在处理复杂查找需求时更加便捷和灵活。