周末过得有点颓,没有读源码。
现在补上。
difference && differenceBy && differenceWith
正文开始前,只想吐槽lodash这波方法封装的操作丝毫没有提高代码的可读性,反而读的更费力了。把功能无限拆分到最小,每个子功能都是一个方法,真的很差评了。
吐槽下这些方法结构如下:
lodash-difference
想想吧,这么多种方法,这么多文件,晕不晕!!!
吐槽完毕,回归正题。
difference
difference的作用用集合简单描述就是:给定两个集合A、B,difference(A) = CA(A∩B)
以下是源码。
difference.js
// 首先判断两个集合是否是数组。如果不是数组直接出门不送,是数组的话进行核心方法baseDifference的比较。
function difference(array, ...values) {
return isArrayLikeObject(array)
? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
: []
}
主文件就是一波天秀。isArrayLikeObject封装了多个方法,核心意思就是判断是不是数组。严谨如lodash,对于判断数组长度还封装了isLength方法来判定数组长度是否为数字。
baseFlatten是用来将数组扁平化的,我的理解是如果数组层次太深,对数组进行一个扁平化处理。
然而对于简单的数组来说,这一步操作就是走过场。尤其是在主代码中,baseFlatten的传参是depth=1,简直多此一举。
需要注意的是,在主方法中,传的两个参数并不是array和values,而是解构后的array和...values。
这意味着。
...values = [2,3]
value = [[2,3]]
接下来是核心代码basedifference
核心中的核心只有以下几行
for (let value of array) {
const computed = iteratee == null ? value : iteratee(value)
value = (comparator || value !== 0) ? value : 0
if (isCommon && computed === computed) {
let valuesIndex = valuesLength
while (valuesIndex--) {
if (values[valuesIndex] === computed) {
continue outer
}
}
result.push(value)
}
}
没错用了双层遍历来判断。
为了实现这双层遍历,对于边界进行了种种处理。
arrayIncludes&arrayIncludesWith方法用来判断数组是否包含某个元素。对于大数组,baseDifference提供了SetCache、CacheHas方法。
/**
* -.difference的基本实现。
*
* @private
* @param {Array} array The array to inspect. 需要被检查的数组
* @param {Array} values The values to exclude. 需要被排除出去的数组
* @param {Function} [iteratee] The iteratee invoked per element. 迭代器函数,调用每个element
* @param {Function} [comparator] The comparator invoked per element. 比较器,也会调用每个ele
* @returns {Array} Returns the new array of filtered values. 返回被过滤调的新的数组
*/
function baseDifference(array, values, iteratee, comparator) {
var index = -1,
includes = arrayIncludes,//方法。
isCommon = true,
length = array.length,
result = [],
valuesLength = values.length;
if (!length) {
return result;
}
if (iteratee) { // 迭代器函数。 这里需要研究arrayMap 和baseUnary方法了
values = arrayMap(values, baseUnary(iteratee)); // 这里提前处理下 while循环里边有个判段,这里提前就遍历,处理数据。
}
if (comparator) { //comparator存在,includes从arrayIncludes =》arrayIncludesWith,
includes = arrayIncludesWith;
isCommon = false;
}
else if (values.length >= LARGE_ARRAY_SIZE) {
//- 大型数组的优化,这里默认理解为超过200就是大数组。大的数组启用缓存。
includes = cacheHas; // includes方法设置为cacheHas处理,这里也是做缓存
isCommon = false;//标示 不是普通方式处理了
values = new SetCache(values);
}
outer:
//切记,比对的是array,values
while (++index < length) {
var value = array[index],//array的一个element
computed = iteratee ? iteratee(value) : value;//如果有迭代器,就处理成为computed
value = (comparator || value !== 0) ? value : 0;
if (isCommon && computed === computed) { // 取出来的数据不是NaN
var valuesIndex = valuesLength;
while (valuesIndex--) {
if (values[valuesIndex] === computed) {
continue outer; //跳会outer,继续执行 因为是求差集,也就是value中的元素,在array不能存在。 这里有相同,array中的当前元素就不应该在结果集里出现。
}
}
result.push(value);
}
else if (!includes(values, computed, comparator)) {
result.push(value);
}
}
return result;
}
相关方法分析
arrayIncludes
/**
* 数组中Array是否包含 value ,
* @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) {
var length = array ? array.length : 0;
return !!length && baseIndexOf(array, value, 0) > -1;
}
// arrayIncludes(["123"],"123") => true
baseIndexOf
/**
* The base implementation of `_.indexOf` without `fromIndex` bounds checks.
* _.indexOf的基本实现,没有fromIndex的边界检查
* @private
* @param {Array} array The array to inspect. 被检查的数组
* @param {*} value The value to search for. 被查找的值
* @param {number} fromIndex The index to search from. 从哪个位置开始search
* @returns {number} Returns the index of the matched value, else `-1`.
*/
function baseIndexOf(array, value, fromIndex) {
if (value !== value) { //NaN就调用baseFindIndex
return baseFindIndex(array, baseIsNaN, fromIndex);
}
var index = fromIndex - 1, // -1
length = array.length; //
while (++index < length) {
if (array[index] === value) {
return index; //遍历判断结果,有符合条件 返回index,否则为-1
}
}
return -1;
}
arrayMap
/**
* A specialized version of `_.map` for arrays without support for iteratee
* shorthands.
*
* @private
* @param {Array} [array] The array to iterate over. 用来被遍历的数组
* @param {Function} iteratee The function invoked per iteration. 迭代器
* @returns {Array} Returns the new mapped array.
*/
function arrayMap(array, iteratee) {
var index = -1,
length = array ? array.length : 0,
result = Array(length); //结果数组
while (++index < length) {
result[index] = iteratee(array[index], index, array); // 这里就遍历了数据,返回遍历的结果。
}
return result;
}
difference方法系列还有另外两个方法:differenceBy和differenceWith
相比之下,differenceBy多了迭代器的方法,differenceWith增加比较器。
但是核心方法都是baseDifference,此处不再赘述。
BTW,baseDifference中arrayIncludesWith本质上是个比较方法。
在普通Difference方法中,实现思路是判断相等。因为differenceWith方法提供了比较器,因而需要使用另外一套比较思路,即arrayIncludesWith方法。
网友评论