Lodash源码阅读-lastIndexOf

Lodash 源码阅读-lastIndexOf

概述

lastIndexOf 是 Lodash 中用于在数组中查找元素的方法,它返回找到的最后一个匹配元素的索引,如果没找到则返回 -1。与 JavaScript 原生的 Array.prototype.lastIndexOf 类似,但提供了更好的兼容性和对 NaN 值的特殊处理。

前置学习

依赖函数

  • strictLastIndexOf:Lodash 的内部从右向左查找函数
  • baseFindIndex:Lodash 的基础查找函数
  • baseIsNaN:Lodash 判断 NaN 的内部函数
  • toInteger:Lodash 的整数转换函数

技术知识

  • JavaScript 数组操作
  • NaN 值的特性(NaN !== NaN)
  • 数组索引的正负值处理
  • 类型转换

源码实现

js 复制代码
function lastIndexOf(array, value, fromIndex) {
  var length = array == null ? 0 : array.length;
  if (!length) {
    return -1;
  }
  var index = length;
  if (fromIndex !== undefined) {
    index = toInteger(fromIndex);
    index =
      index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);
  }
  return value === value
    ? strictLastIndexOf(array, value, index)
    : baseFindIndex(array, baseIsNaN, index, true);
}

实现思路

lastIndexOf 函数的实现主要分为三个步骤:

  1. 首先处理边界情况,确保数组有效
  2. 然后处理起始位置参数,确保索引在有效范围内
  3. 最后根据查找值的类型(是否为 NaN)选择不同的查找策略

源码解析

1. 边界处理

js 复制代码
var length = array == null ? 0 : array.length;
if (!length) {
  return -1;
}
  • 使用 array == null 同时检查 nullundefined
  • 如果数组为空或无效,直接返回 -1

2. 索引处理

js 复制代码
var index = length;
if (fromIndex !== undefined) {
  index = toInteger(fromIndex);
  index =
    index < 0 ? nativeMax(length + index, 0) : nativeMin(index, length - 1);
}
  • 默认从数组末尾开始查找
  • 使用 toInteger 确保索引为整数
  • 处理负数索引:从数组末尾开始计算,且不小于 0
  • 处理正数索引:确保不超过数组最大索引

3. 查找策略

js 复制代码
return value === value
  ? strictLastIndexOf(array, value, index)
  : baseFindIndex(array, baseIsNaN, index, true);
  • 利用 NaN !== NaN 的特性判断是否为 NaN
  • 普通值使用 strictLastIndexOf 查找
  • NaN 值使用 baseFindIndex 配合 baseIsNaN 查找

应用场景

  1. 查找数组中最后一个匹配元素的位置
js 复制代码
// 在用户操作历史中查找最后一次点击某个按钮的记录
const userActions = ["click", "scroll", "click", "hover", "click"];
const lastClickIndex = _.lastIndexOf(userActions, "click"); // => 4

// 在商品列表中查找最后一个库存为0的商品
const products = [
  { id: 1, stock: 0 },
  { id: 2, stock: 5 },
  { id: 3, stock: 0 },
];
const lastOutOfStockIndex = _.lastIndexOf(products, { id: 3, stock: 0 }); // => 2
  1. 处理包含 NaN 的数组
js 复制代码
const arr = [1, NaN, 3, NaN];
const lastNaNIndex = _.lastIndexOf(arr, NaN); // 找到最后一个 NaN 的位置
  1. 从指定位置向前查找
js 复制代码
// 在聊天记录中查找最近的特定消息
const messages = ["hello", "world", "hello", "world"];
const lastHelloBeforeIndex = _.lastIndexOf(messages, "hello", 2); // => 0

// 在文件路径中查找最后一个目录分隔符
const path = "src/components/App.vue";
const lastSlashIndex = _.lastIndexOf(path, "/", path.length - 1); // => 13

总结

实现特点

  1. 健壮的空值处理
  2. 灵活的索引计算
  3. 特殊的 NaN 值处理
  4. 模块化的设计思路

设计原则

  1. 单一职责原则:将查找逻辑分离到专门的函数中
  2. 防御性编程:对各种边界情况进行处理
  3. 代码复用:通过基础函数组合实现复杂功能
相关推荐
子兮曰2 小时前
async/await高级模式:async迭代器、错误边界与并发控制
前端·javascript·github
恋猫de小郭3 小时前
2026 Flutter VS React Native ,同时在 AI 时代 VS Native 开发,你没见过的版本
android·前端·flutter
冬奇Lab4 小时前
PowerManagerService(上):电源状态与WakeLock管理
android·源码阅读
GIS之路5 小时前
ArcGIS Pro 中的 Notebooks 入门
前端
IT_陈寒6 小时前
React状态管理终极对决:Redux vs Context API谁更胜一筹?
前端·人工智能·后端
Kagol7 小时前
TinyVue 支持 Skills 啦!现在你可以让 AI 使用 TinyVue 组件搭建项目
前端·agent·ai编程
柳杉7 小时前
从零打造 AI 全球趋势监测大屏
前端·javascript·aigc
simple_lau7 小时前
Cursor配置MasterGo MCP:一键读取设计稿生成高还原度前端代码
前端·javascript·vue.js
睡不着先生7 小时前
如何设计一个真正可扩展的表单生成器?
前端·javascript·vue.js
天蓝色的鱼鱼7 小时前
模块化与组件化:90%的前端开发者都没搞懂的本质区别
前端·架构·代码规范