由于difference系列引用的方法过多,所以在之前的文章《lodash源码解析:baseDifference、map》中把公用的私有方法baseDifference
单独进行了分析,本篇继续顺着分析difference系列方法。
引用的私有方法
baseDifference
difference系列方法的核心基础方法,见前一篇文章《lodash源码解析:baseDifference、map》
getTag
返回目标的tag(比如[object Null]
),见之前文章《lodash源码解析:chunk、slice、toInteger、toFinite、toNumber、isObject、isSymbol》
isFlattenable
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import isArguments from '../isArguments.js'
const spreadableSymbol = Symbol.isConcatSpreadable
function isFlattenable(value) { return Array.isArray(value) || isArguments(value) || !!(value && value[spreadableSymbol]) }
export default isFlattenable
|
baseFlatten
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| import isFlattenable from './isFlattenable.js'
function baseFlatten(array, depth, predicate, isStrict, result) { predicate || (predicate = isFlattenable) result || (result = [])
if (array == null) { return result }
for (const value of array) { if (depth > 0 && predicate(value)) { if (depth > 1) { baseFlatten(value, depth - 1, predicate, isStrict, result) } else { result.push(...value) } } else if (!isStrict) { result[result.length] = value } } return result }
export default baseFlatten
|
引用的公开方法
isArguments
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import getTag from './.internal/getTag.js' import isObjectLike from './isObjectLike.js'
function isArguments(value) { return isObjectLike(value) && getTag(value) == '[object Arguments]' }
export default isArguments
|
isObjectLike
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
function isObjectLike(value) { return typeof value === 'object' && value !== null }
export default isObjectLike
|
isLength
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| const MAX_SAFE_INTEGER = 9007199254740991
function isLength(value) { return typeof value === 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER }
export default isLength
|
isArrayLike
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| import isLength from './isLength.js'
function isArrayLike(value) { return value != null && typeof value !== 'function' && isLength(value.length) }
export default isArrayLike
|
isArrayLikeObject
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import isArrayLike from './isArrayLike.js' import isObjectLike from './isObjectLike.js'
function isArrayLikeObject(value) { return isObjectLike(value) && isArrayLike(value) }
export default isArrayLikeObject
|
last
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
function last(array) { const length = array == null ? 0 : array.length return length ? array[length - 1] : undefined }
export default last
|
difference 系列
该系列的三个方法都用了三个核心内置方法:isArrayLikeObject
、baseDifference
和baseFlatten
,isArrayLikeObject
主要用于忽略掉values中非类数组对象的value,baseDifference
主要用执行排除的核心逻辑,baseFlatten
用于把参数values合成为一个数组。
difference
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| import baseDifference from './.internal/baseDifference.js' import baseFlatten from './.internal/baseFlatten.js' import isArrayLikeObject from './isArrayLikeObject.js'
function difference(array, ...values) { return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) : [] }
export default difference
|
differenceBy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import baseDifference from './.internal/baseDifference.js' import baseFlatten from './.internal/baseFlatten.js' import isArrayLikeObject from './isArrayLikeObject.js' import last from './last.js'
function differenceBy(array, ...values) { let iteratee = last(values) if (isArrayLikeObject(iteratee)) { iteratee = undefined } return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), iteratee) : [] }
export default differenceBy
|
differenceWith
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import baseDifference from './.internal/baseDifference.js' import baseFlatten from './.internal/baseFlatten.js' import isArrayLikeObject from './isArrayLikeObject.js' import last from './last.js'
function differenceWith(array, ...values) { let comparator = last(values) if (isArrayLikeObject(comparator)) { comparator = undefined } return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) : [] }
export default differenceWith
|
原生实现
last
虽然不知道为什么last
这种也会专门有个方法,但是You-Dont-Need-Lodash-Underscore中也给出了原生的写法。
1 2 3 4 5 6 7 8 9 10 11
|
const numbers = [1, 2, 3, 4, 5]; numbers[numbers.length - 1];
numbers.slice(-1)[0];
[].concat(numbers).pop()
|
difference
简单的原生difference的实现是使用了reduce
和filter
:
- 首先
array
数组的第一个值([1, 2, 3, 4, 5]
)应该是对应于lodash-difference
中的array
参数,剩余的对应values
。
reduce
函数没有提供initialValue
参数,所以第一个值为初始值。
- 每次循环时
a
为累计器,b
为正在处理的元素。
- 每次循环中使用了
filter
对累计器a
进行了筛选内循环。
a
的每一项c
筛选时,都必须不能包含在b
中。
- 完成。
1 2 3 4 5
| let arrays = [[1, 2, 3, 4, 5], [5, 2, 10]]; const result = arrays.reduce((a, b) => a.filter(c => !b.includes(c))); console.log(result);
|