Lodash源码阅读-findIndex

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. 先检查数组是否为空或不存在,是的话直接返回 -1
  2. 处理起始位置参数 fromIndex
    • 如果没提供,默认从 0 开始查找
    • 如果提供了,转换为整数
    • 如果是负数,就从数组末尾往前算,但不小于 0
  3. 调用 baseFindIndex 进行实际的查找,传入:
    • 要查找的数组
    • 转换后的断言函数(通过 getIteratee 处理)
    • 计算好的起始查找位置

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

源码解析

参数检查和长度判断

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

这段代码先判断数组是否存在。使用 array == null 可以同时检查 nullundefined。如果数组不存在,就把长度设为 0;否则,获取数组的实际长度。

如果长度为 0(表示数组为空或不存在),直接返回 -1,表示找不到元素。这是一个常见的优化手段,避免对空数组做无谓的处理。

起始位置处理

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

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

  1. 如果 fromIndexnullundefined,默认使用 0
  2. 否则用 toInteger 把它转成整数
  3. 如果转换后是负数,从数组末尾向前计算:
    • length + index 表示从末尾向前数 |index| 个位置
    • nativeMax(length + index, 0) 确保结果不小于 0

这种设计让函数支持负数索引,比如 -1 表示从最后一个元素开始找,-2 表示从倒数第二个开始找。

核心查找逻辑

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

最后调用 baseFindIndex 函数进行实际查找,传入三个参数:

  1. array:要查找的数组
  2. getIteratee(predicate, 3):转换后的断言函数
    • getIteratee 会根据 predicate 类型返回合适的迭代器函数
    • 参数 3 表示迭代器函数接收三个参数:元素值、索引和数组本身
  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 中一个简单却强大的数组工具函数,它的亮点包括:

  1. 灵活的参数形式:支持函数、对象、数组、字符串等多种断言形式
  2. 合理的参数处理:妥善处理默认值和负数索引
  3. 性能优化:对空数组快速返回,避免无谓计算
  4. 代码分层:参数处理和核心逻辑分离,职责明确

这些特点使得 findIndex 比原生的 Array.prototype.findIndex 更强大,在处理复杂查找需求时更加便捷和灵活。

相关推荐
GIS之路9 分钟前
GDAL 实现矢量裁剪
前端·python·信息可视化
是一个Bug12 分钟前
后端开发者视角的前端开发面试题清单(50道)
前端
Amumu1213814 分钟前
React面向组件编程
开发语言·前端·javascript
持续升级打怪中36 分钟前
Vue3 中虚拟滚动与分页加载的实现原理与实践
前端·性能优化
GIS之路39 分钟前
GDAL 实现矢量合并
前端
hxjhnct42 分钟前
React useContext的缺陷
前端·react.js·前端框架
冰暮流星1 小时前
javascript逻辑运算符
开发语言·javascript·ecmascript
前端 贾公子1 小时前
从入门到实践:前端 Monorepo 工程化实战(4)
前端
菩提小狗1 小时前
Sqlmap双击运行脚本,双击直接打开。
前端·笔记·安全·web安全
前端工作日常1 小时前
我学习到的AG-UI的概念
前端