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. 代码复用:通过基础函数组合实现复杂功能
相关推荐
阿珊和她的猫1 小时前
v-scale-scree: 根据屏幕尺寸缩放内容
开发语言·前端·javascript
加班是不可能的,除非双倍日工资6 小时前
css预编译器实现星空背景图
前端·css·vue3
wyiyiyi6 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
gnip6 小时前
vite和webpack打包结构控制
前端·javascript
excel7 小时前
在二维 Canvas 中模拟三角形绕 X、Y 轴旋转
前端
阿华的代码王国7 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
一条上岸小咸鱼7 小时前
Kotlin 基本数据类型(三):Booleans、Characters
android·前端·kotlin
Jimmy7 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
ZXT8 小时前
promise & async await总结
前端
Jerry说前后端8 小时前
RecyclerView 性能优化:从原理到实践的深度优化方案
android·前端·性能优化