A modern JavaScript utility library delivering modularity, performance & extras.
lodash
是一个一致性、模块化、高性能的JavaScript
实用工具库
# 一、环境准备
lodash
版本v4.0.0
通过
github1s
网页可以 查看 (opens new window)lodash - difference
源码调试测试用例可以
clone
到本地
git clone https://github.com/lodash/lodash.git
cd axios
npm install
npm run test
# 二、结构分析
这是一张 difference
依赖引用路径图,相对复杂一些,按照功能划分,大致包括cache模块、index模块和flatten模块。接下来会自底向上分析各个依赖模块。由于依赖较多,篇幅较长,将按照模块分成四个部分,本篇主要讲述 Index
模块,包含 arrayIncludes
、baseIndexOf
、baseFindIndex
、baseIsNaN
、strictIndexOf
。
# 三、函数研读
# 1. strictIndexOf 模块
indexOf的一个特殊版本,它执行严格的相等用于比较value,比如===
/**
* @private
* @param {Array} array The array to inspect.
* @param {*} value The value to search for.
* @param {number} fromIndex The index to search from.
* @returns {number} Returns the index of the matched value, else `-1`.
*/
function strictIndexOf(array, value, fromIndex) {
let index = fromIndex - 1
const { length } = array
while (++index < length) {
if (array[index] === value) {
return index
}
}
return -1
}
export default strictIndexOf
- 重点关注MDN - Strict equality (===) (opens new window),全等运算符与相等运算符
==
最显著的区别是,如果操作数的类型不同,==
运算符会在比较之前尝试将它们转换为相同的类型。
# 2. baseIsNaN 模块
'isNaN'的基本实现,不支持数字对象
/**
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
*/
function baseIsNaN(value) {
return value !== value
}
export default baseIsNaN
- 重点关注全局属性 NaN (opens new window),
NaN
的值表示不是一个数字(Not-A-Number),在现代浏览器中(ES5中), NaN 属性是一个不可配置(non-configurable),不可写(non-writable)的属性 - 在执行自比较之中:NaN,也只有NaN,比较之中不等于它自己,
NaN === NaN; // false
# 3. baseFindIndex 模块
'findIndex'和'findLastIndex'的基本实现
/**
* @private
* @param {Array} array The array to inspect.
* @param {Function} predicate 每次迭代调用的函数
* @param {number} fromIndex The index to search from.
* @param {boolean} [fromRight] Specify iterating from right to left.
* @returns {number} Returns the index of the matched value, else `-1`.
*/
function baseFindIndex(array, predicate, fromIndex, fromRight) {
const { length } = array
let index = fromIndex + (fromRight ? 1 : -1)
while ((fromRight ? index-- : ++index < length)) {
if (predicate(array[index], index, array)) {
return index
}
}
return -1
}
export default baseFindIndex
- 重点关注
index = fromIndex + (fromRight ? 1 : -1)
,由于支持从右向左的迭代,起始index
应该+1
以防止index--
越过0
从而进入死循环,同理从左侧查起要确保查到array[0]
从而起始index
需要加一
# 4. baseIndexOf 模块
没有fromIndex
边界检查的indexOf
的基本实现
import baseFindIndex from './baseFindIndex.js'
import baseIsNaN from './baseIsNaN.js'
import strictIndexOf from './strictIndexOf.js'
/**
* @private
* @param {Array} array The array to inspect.
* @param {*} value The value to search for.
* @param {number} fromIndex The index to search from.
* @returns {number} Returns the index of the matched value, else `-1`.
*/
function baseIndexOf(array, value, fromIndex) {
return value === value
? strictIndexOf(array, value, fromIndex)
: baseFindIndex(array, baseIsNaN, fromIndex)
}
export default baseIndexOf
value
如果不是NaN
,进入strictIndexOf
,从array[fromIndex]
开始按序严格比较是否与value
相等,若相等返回对应index
,否则返回-1
value
如果是NaN
,进入baseFindIndex
,将baseIsNaN
作为baseFindIndex
的入参迭代函数predicate
并开始从array[fromIndex]
开始判断是否为NaN
,若找到NaN
就返回对应index
,否则返回-1
Tips:可以看到 baseFindIndex
模块中的有些形参是没有用到的,比如查找时是按照从左往右的顺序查找,并没有传入 fromRight
,但提前占了坑,体现了很好的扩展性🐶
# 5. arrayIncludes 模块
不支持从数组指定位置搜索的includes
import baseIndexOf from './baseIndexOf.js'
/**
* @private
* @param {Array} [array] The array to inspect.
* @param {*} target The value to search for.
* @returns {boolean} Returns `true` if `target` is found, else `false`.
*/
function arrayIncludes(array, value) {
const length = array == null ? 0 : array.length
return !!length && baseIndexOf(array, value, 0) > -1
}
export default arrayIncludes
- 如果 length 不存在(包含null、0)或者没有找到 index 都会返回false
Tips:!!
运算符表示逻辑非的取反运算,如!!obj
与 obj != null && typeof obj === undefined && obj != "" && obj != false
在计算上等价