Lodash 源码阅读-assocIndexOf
概述
assocIndexOf 是 Lodash 内部使用的辅助函数,用于在键值对数组中查找指定键所在的索引位置。它是 ListCache 缓存实现的核心查找函数,提供了高效的键查找功能。
前置学习
- eq:判断两个值是否相等的辅助函数,使用 SameValueZero 算法实现
技术知识:
- JavaScript 数组的遍历
- JavaScript 中的相等性比较
- SameValueZero 算法
源码实现
javascript
/**
* Gets the index at which the `key` is found in `array` of key-value pairs.
*
* @private
* @param {Array} array The array to inspect.
* @param {*} key The key to search for.
* @returns {number} Returns the index of the matched value, else `-1`.
*/
function assocIndexOf(array, key) {
var length = array.length;
while (length--) {
if (eq(array[length][0], key)) {
return length;
}
}
return -1;
}
实现思路
assocIndexOf 函数的实现思路非常直接:
- 从数组的末尾向前遍历
- 对于每个元素,比较其第一个值(键)是否与目标键相等
- 如果找到匹配项,立即返回当前索引
- 如果遍历完整个数组都没有找到匹配项,返回 -1 表示未找到
函数使用 eq 函数进行键的比较,这确保了正确处理特殊情况(如 NaN)。
源码解析
函数接收两个参数:
array
: 要搜索的键值对数组,每个元素都是[key, value]
形式key
: 要查找的键
让我们详细解析实现:
-
var length = array.length;
: 获取数组的长度。 -
while (length--) { ... }
: 从数组末尾向前遍历。每次迭代会先使用当前值,然后将length - 1
。这种从后向前遍历的方式是一种常见的优化技巧,因为通常最近添加的元素更可能被查找(利用了时间局部性原理)。 -
if (eq(array[length][0], key)) { ... }
:array[length]
获取当前位置的键值对[key, value]
array[length][0]
获取这个键值对的键eq(array[length][0], key)
使用 eq 函数比较当前元素的键与目标键是否相等
-
return length;
: 如果找到匹配项,立即返回当前索引。 -
return -1;
: 如果遍历完整个数组都没有找到匹配项,返回 -1 表示未找到。
例如:
javascript
const arr = [
["name", "John"],
["age", 30],
["city", "New York"],
];
assocIndexOf(arr, "age"); // 返回 1
assocIndexOf(arr, "gender"); // 返回 -1
应用场景
assocIndexOf 在 Lodash 内部主要用于 ListCache 缓存实现中,是其核心查找机制。它在以下场景中被使用:
- ListCache.has(key): 检查缓存中是否存在特定键
- ListCache.get(key): 获取特定键的值
- ListCache.set(key, value): 设置或更新键值对
- ListCache.delete(key): 删除指定键的键值对
总结
assocIndexOf 是一个简单而高效的数组查找函数,通过从后向前遍历实现键的快速查找。它具有以下特点:
- 简单高效:代码简洁,实现直观,性能良好
- 反向遍历:利用时间局部性原理,从数组末尾向前查找,优化常见使用场景
- 健壮处理:通过 eq 函数处理各种相等情况,包括 NaN 的正确处理
从设计角度看,我们可以学习到:
- 职责单一:函数只负责在数组中查找键,不处理其他逻辑
- 封装复杂度:将键的比较逻辑封装在 eq 函数中,简化主函数实现
- 优化常见情况:通过反向遍历优化最常见的访问模式
在实际开发中,我们也可以参考这种实现方式,为自定义的数据结构提供高效的查找机制。甚至在处理简单的键值数据时,这种数组实现对于小数据量可能比 Map 或对象更高效。