Lodash 源码阅读-sortedIndexOf
概述
sortedIndexOf
是 Lodash 里的一个查找函数,它在已排序的数组中查找某个值的位置。跟普通的 indexOf
比起来,它利用了数组已经排好序的特点,用二分查找的方式快速找到目标,速度比普通查找快很多(时间复杂度从 O(n) 降到 O(log n))。
前置学习
依赖函数
- baseSortedIndex:内部的二分查找函数,用来找值应该插入的位置
- eq:判断两个值是否相等的函数,能正确处理各种特殊情况(比如 NaN)
技术知识
- 二分查找:一种在有序数据中快速查找的算法,每次把查找范围缩小一半
- 相等性比较:JavaScript 中判断值相等的方法和陷阱
- 边界检查:避免数组索引越界的常见技巧
源码实现
javascript
/**
* This method is like `_.indexOf` except that it performs a binary
* search on a sorted `array`.
*
* @static
* @memberOf _
* @since 4.0.0
* @category Array
* @param {Array} array The array to inspect.
* @param {*} value The value to search for.
* @returns {number} Returns the index of the matched value, else `-1`.
* @example
*
* _.sortedIndexOf([4, 5, 5, 5, 6], 5);
* // => 1
*/
function sortedIndexOf(array, value) {
var length = array == null ? 0 : array.length;
if (length) {
var index = baseSortedIndex(array, value);
if (index < length && eq(array[index], value)) {
return index;
}
}
return -1;
}
实现思路
sortedIndexOf
的思路很直接:
- 先检查数组是否为空,空的就直接返回 -1
- 用二分查找找到值应该在的位置(第一个大于等于这个值的位置)
- 检查一下这个位置上的值是否就是我们要找的值:
- 是的话,返回这个位置
- 不是的话,说明数组里没有这个值,返回 -1
这种方法比普通的从头找到尾的方式快多了,特别是在大数组中查找时。
源码解析
边界检查
javascript
var length = array == null ? 0 : array.length;
if (length) {
// 查找逻辑
}
return -1;
先看数组是否为空:
- 如果传入的
array
是null
或undefined
,就把长度设为 0 - 只有数组有内容时才进行查找
- 如果数组是空的,直接返回 -1
这个检查很重要,避免了访问不存在数组的属性而报错。
查找和匹配
javascript
var index = baseSortedIndex(array, value);
if (index < length && eq(array[index], value)) {
return index;
}
这部分是核心逻辑:
- 用
baseSortedIndex
找到值应该在的位置- 这个函数使用二分查找,找到的是第一个大于等于目标值的位置
- 检查两个条件:
- 找到的位置是否在数组范围内(
index < length
) - 该位置的值是否等于要找的值(
eq(array[index], value)
)
- 找到的位置是否在数组范围内(
- 只有当这两个条件都满足时,才返回找到的位置
举个例子说明 index < length
的用处: 假设我们有数组 [1, 3, 5, 7]
,要查找值 9
:
baseSortedIndex
会返回4
(因为 9 应该插入到数组末尾)- 但数组长度只有
4
,所以index < length
条件不满足(4 < 4 为 false) - 这就避免了访问
array[4]
(不存在的元素),防止出现越界错误 - 函数会直接返回
-1
,表示没找到
这个检查确保了即使要查找的值比数组中所有元素都大,函数也能正确处理。
eq
函数比普通的 ===
更智能,能正确处理 NaN 等特殊情况。
返回结果
javascript
return -1;
如果没找到匹配的值,就返回 -1,这和 JavaScript 原生的 indexOf
行为一致。
总结
sortedIndexOf
这个小函数展示了数据结构和算法的重要性:
-
利用数据特性
- 知道数组已排序后,可以用更高效的算法
- 二分查找把时间复杂度从 O(n) 降到 O(log n)
-
健壮性设计
- 做好边界检查和异常处理
- 用
eq
函数解决 JavaScript 相等比较的各种坑
-
API 设计
- 保持与原生
indexOf
相同的接口风格 - 函数名清晰地表明它的用途和适用条件
- 保持与原生
这种专门针对特定场景优化的函数,是 Lodash 这类工具库价值的体现 ------ 让常见操作变得更简单、更高效。