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 更强大,在处理复杂查找需求时更加便捷和灵活。

相关推荐
暖和_白开水14 小时前
数据分析agent (七):contextvars 模块上下文request_id
java·前端·数据分析
程序员黑豆15 小时前
鸿蒙应用开发:Flex 组件从入门到实战
前端·华为·harmonyos
大爱编程♡15 小时前
Vue3+TypeScript+element-plus的文章管理项目(配后端接口)-退出及文章管理页面
前端·javascript·typescript
先吃饱再说15 小时前
React 组件设计:从 Props 传递到组件封装
前端·react.js
独立开阀者_FwtCoder15 小时前
最近做了一个健身小程序:智形健身助手,健身的佬们来提点意见
前端·javascript·github
舒灿15 小时前
喵ing Tab 1.0.0 正式发布
前端·ai编程
葡萄城技术团队15 小时前
当表格数据来自外部系统:SpreadJS 如何让异步公式一键刷新
前端
小徐_233315 小时前
Open Wot 1.0.5 发布:让 AI 接入 wot-ui,只需要两条命令
前端·uni-app·ai编程
Android研究员15 小时前
Android进阶之事件分发机制深度剖析
android·前端·面试
Csvn15 小时前
🛡️ 前端水印方案全解析——从「能被 F12 删掉」到「删不掉」的反破解实战
前端