周末過得有點頹,沒有讀源碼。
現(xiàn)在補(bǔ)上。
difference && differenceBy && differenceWith
正文開始前,只想吐槽lodash這波方法封裝的操作絲毫沒有提高代碼的可讀性,反而讀的更費力了。把功能無限拆分到最小,每個子功能都是一個方法,真的很差評了。
吐槽下這些方法結(jié)構(gòu)如下:

想想吧,這么多種方法,這么多文件,暈不暈?。?!
吐槽完畢,回歸正題。
difference
difference的作用用集合簡單描述就是:給定兩個集合A、B,difference(A) = CA(A∩B)
以下是源碼。
difference.js
// 首先判斷兩個集合是否是數(shù)組。如果不是數(shù)組直接出門不送,是數(shù)組的話進(jìn)行核心方法baseDifference的比較。
function difference(array, ...values) {
return isArrayLikeObject(array)
? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
: []
}
主文件就是一波天秀。isArrayLikeObject封裝了多個方法,核心意思就是判斷是不是數(shù)組。嚴(yán)謹(jǐn)如lodash,對于判斷數(shù)組長度還封裝了isLength方法來判定數(shù)組長度是否為數(shù)字。
baseFlatten是用來將數(shù)組扁平化的,我的理解是如果數(shù)組層次太深,對數(shù)組進(jìn)行一個扁平化處理。
然而對于簡單的數(shù)組來說,這一步操作就是走過場。尤其是在主代碼中,baseFlatten的傳參是depth=1,簡直多此一舉。
需要注意的是,在主方法中,傳的兩個參數(shù)并不是array和values,而是解構(gòu)后的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)
}
}
沒錯用了雙層遍歷來判斷。
為了實現(xiàn)這雙層遍歷,對于邊界進(jìn)行了種種處理。
arrayIncludes&arrayIncludesWith方法用來判斷數(shù)組是否包含某個元素。對于大數(shù)組,baseDifference提供了SetCache、CacheHas方法。
/**
* -.difference的基本實現(xiàn)。
*
* @private
* @param {Array} array The array to inspect. 需要被檢查的數(shù)組
* @param {Array} values The values to exclude. 需要被排除出去的數(shù)組
* @param {Function} [iteratee] The iteratee invoked per element. 迭代器函數(shù),調(diào)用每個element
* @param {Function} [comparator] The comparator invoked per element. 比較器,也會調(diào)用每個ele
* @returns {Array} Returns the new array of filtered values. 返回被過濾調(diào)的新的數(shù)組
*/
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) { // 迭代器函數(shù)。 這里需要研究arrayMap 和baseUnary方法了
values = arrayMap(values, baseUnary(iteratee)); // 這里提前處理下 while循環(huán)里邊有個判段,這里提前就遍歷,處理數(shù)據(jù)。
}
if (comparator) { //comparator存在,includes從arrayIncludes =》arrayIncludesWith,
includes = arrayIncludesWith;
isCommon = false;
}
else if (values.length >= LARGE_ARRAY_SIZE) {
//- 大型數(shù)組的優(yōu)化,這里默認(rèn)理解為超過200就是大數(shù)組。大的數(shù)組啟用緩存。
includes = cacheHas; // includes方法設(shè)置為cacheHas處理,這里也是做緩存
isCommon = false;//標(biāo)示 不是普通方式處理了
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) { // 取出來的數(shù)據(jù)不是NaN
var valuesIndex = valuesLength;
while (valuesIndex--) {
if (values[valuesIndex] === computed) {
continue outer; //跳會outer,繼續(xù)執(zhí)行 因為是求差集,也就是value中的元素,在array不能存在。 這里有相同,array中的當(dāng)前元素就不應(yīng)該在結(jié)果集里出現(xiàn)。
}
}
result.push(value);
}
else if (!includes(values, computed, comparator)) {
result.push(value);
}
}
return result;
}
相關(guān)方法分析
arrayIncludes
/**
* 數(shù)組中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的基本實現(xiàn),沒有fromIndex的邊界檢查
* @private
* @param {Array} array The array to inspect. 被檢查的數(shù)組
* @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就調(diào)用baseFindIndex
return baseFindIndex(array, baseIsNaN, fromIndex);
}
var index = fromIndex - 1, // -1
length = array.length; //
while (++index < length) {
if (array[index] === value) {
return index; //遍歷判斷結(jié)果,有符合條件 返回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. 用來被遍歷的數(shù)組
* @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); //結(jié)果數(shù)組
while (++index < length) {
result[index] = iteratee(array[index], index, array); // 這里就遍歷了數(shù)據(jù),返回遍歷的結(jié)果。
}
return result;
}
difference方法系列還有另外兩個方法:differenceBy和differenceWith
相比之下,differenceBy多了迭代器的方法,differenceWith增加比較器。
但是核心方法都是baseDifference,此處不再贅述。
BTW,baseDifference中arrayIncludesWith本質(zhì)上是個比較方法。
在普通Difference方法中,實現(xiàn)思路是判斷相等。因為differenceWith方法提供了比較器,因而需要使用另外一套比較思路,即arrayIncludesWith方法。