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
函数的实现主要分为三个步骤:
- 首先处理边界情况,确保数组有效
- 然后处理起始位置参数,确保索引在有效范围内
- 最后根据查找值的类型(是否为 NaN)选择不同的查找策略
源码解析
1. 边界处理
js
var length = array == null ? 0 : array.length;
if (!length) {
return -1;
}
- 使用
array == null
同时检查null
和undefined
- 如果数组为空或无效,直接返回 -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
查找
应用场景
- 查找数组中最后一个匹配元素的位置
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
- 处理包含 NaN 的数组
js
const arr = [1, NaN, 3, NaN];
const lastNaNIndex = _.lastIndexOf(arr, NaN); // 找到最后一个 NaN 的位置
- 从指定位置向前查找
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
总结
实现特点
- 健壮的空值处理
- 灵活的索引计算
- 特殊的 NaN 值处理
- 模块化的设计思路
设计原则
- 单一职责原则:将查找逻辑分离到专门的函数中
- 防御性编程:对各种边界情况进行处理
- 代码复用:通过基础函数组合实现复杂功能