4
_.difference(array, [values])
difference方法通過SameValueZero方法來比較,找到給定數組中其他參數數組沒有的元素,然后將這些元素組成新數組返回。
參數
array (Array): 用來檢查的數組
[values] (...Array): 用來排除的數組
返回值
(Array):返回一個包含過濾值的新數組
例子
_.difference([2, 1], [2, 3]); // => [1]
源代碼:
difference.js
import baseDifference from './.internal/baseDifference.js' import baseFlatten from './.internal/baseFlatten.js' import isArrayLikeObject from './isArrayLikeObject.js' /** * Creates an array of `array` values not included in the other given arrays * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) * for equality comparisons. The order and references of result values are * determined by the first array. * * **Note:** Unlike `pullAll`, this method returns a new array. * * @since 0.1.0 * @category Array * @param {Array} array The array to inspect. * @param {...Array} [values] The values to exclude. * @returns {Array} Returns the new array of filtered values. * @see union, unionBy, unionWith, without, xor, xorBy, xorWith, * @example * * difference([2, 1], [2, 3]) * // => [1] */ //通過比較給定數組與其他數組,新建一個數組,其中元素是給定數組獨特於其他數組的元素 //也就是求數組的差集 function difference(array, ...values) {//array用來檢測的給定數組,values用來排除元素的其他數組 return isArrayLikeObject(array) ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) : [] //判斷array是不是array-like對象,如果不是,直接返回空數組 //如果array是array-like對象,就執行baseDifference方法,第一個參數是array對象,第二個參數是把其他參數數組組成的數組展開一層后形成的數組 } export default difference
isArrayLikeObject.js
用於判斷一個值是否是一個array-like對象
import isArrayLike from './isArrayLike.js' import isObjectLike from './isObjectLike.js' /** * This method is like `isArrayLike` except that it also checks if `value` * is an object. * * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an array-like object, * else `false`. * @example * * isArrayLikeObject([1, 2, 3]) * // => true * * isArrayLikeObject(document.body.children) * // => true * * isArrayLikeObject('abc') * // => false * * isArrayLikeObject(Function) * // => false */ //判斷一個值是不是一個array-like對象 //isObjectLike判斷一個值是否是一個object-like,規則是:typeof返回object,並且不是null //isArrayLike判斷一個值是否是一個array-like,規則:不等於null,不是function類型,並且有length屬性,length是大於0小於Number.MAX_SAFE_INTEGER的整數 function isArrayLikeObject(value) { return isObjectLike(value) && isArrayLike(value) } export default isArrayLikeObject
isArrayLike.js
import isLength from './isLength.js' /** * Checks if `value` is array-like. A value is considered array-like if it's * not a function and has a `value.length` that's an integer greater than or * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. * * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is array-like, else `false`. * @example * * isArrayLike([1, 2, 3]) * // => true * * isArrayLike(document.body.children) * // => true * * isArrayLike('abc') * // => true * * isArrayLike(Function) * // => false */ //判斷一個值是否是一個array-like //規則:不等於null,不是function類型,並且有length屬性,length是大於0小於Number.MAX_SAFE_INTEGER的整數 function isArrayLike(value) { return value != null && typeof value != 'function' && isLength(value.length) } export default isArrayLike
isLength.js
/** Used as references for various `Number` constants. */ const MAX_SAFE_INTEGER = 9007199254740991 /** * Checks if `value` is a valid array-like length. * * **Note:** This method is loosely based on * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). * * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. * @example * * isLength(3) * // => true * * isLength(Number.MIN_VALUE) * // => false * * isLength(Infinity) * // => false * * isLength('3') * // => false */ //判斷一個值是否是一個有效的array-like對象的length屬性 //是數字且大於0小於Number.MAX_SAFE_INTEGER的整數 function isLength(value) { return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER } export default isLength
isObjectLike.js
/** * Checks if `value` is object-like. A value is object-like if it's not `null` * and has a `typeof` result of "object". * * @since 4.0.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is object-like, else `false`. * @example * * isObjectLike({}) * // => true * * isObjectLike([1, 2, 3]) * // => true * * isObjectLike(Function) * // => false * * isObjectLike(null) * // => false */ //判斷一個值是否是一個object-like,規則是:typeof返回object,並且不是null function isObjectLike(value) { return typeof value == 'object' && value !== null } export default isObjectLike
baseFlatten.js
用於展開數組的方法
import isFlattenable from './isFlattenable.js' /** * The base implementation of `flatten` with support for restricting flattening. * * @private * @param {Array} array The array to flatten. * @param {number} depth The maximum recursion depth. * @param {boolean} [predicate=isFlattenable] The function invoked per iteration. * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks. * @param {Array} [result=[]] The initial result value. * @returns {Array} Returns the new flattened array. */ //這個方法用於數組展開 //array需要展開操作的數組,depth需要展開的層數 //predicate用於判斷值是否可展開 //isStrict標識用於判斷是否約束值必須通過predicate方法的檢查 function baseFlatten(array, depth, predicate, isStrict, result) { predicate || (predicate = isFlattenable)//predicate每次循環都會調用,用來判斷當前值是否是一個可展開的array-like對象 result || (result = []) if (array == null) {//需要展開的數組是空,就返回空數組 return result } for (const value of array) { if (depth > 0 && predicate(value)) {//如果展開層數大於0且當前循環值可展開 if (depth > 1) {//如果展開層數大於一層就繼續遞歸調用,層數減一 // Recursively flatten arrays (susceptible to call stack limits). baseFlatten(value, depth - 1, predicate, isStrict, result) } else {//如果只展開一層,就展開后push到result里 result.push(...value) } } else if (!isStrict) {//如果沒有傳遞isStrict標識,就直接講當前循環值push入結果數組 result[result.length] = value } } return result } export default baseFlatten
isFlattenable.js
import isArguments from '../isArguments.js' /** Built-in value reference. */ const spreadableSymbol = Symbol.isConcatSpreadable /** * Checks if `value` is a flattenable `arguments` object or array. * * @private * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is flattenable, else `false`. */ //檢查一個變量是否是一個可展開的對象或者數組 function isFlattenable(value) { return Array.isArray(value) || isArguments(value) || !!(spreadableSymbol && value && value[spreadableSymbol]) //如果是數組,則可展開 //如果是arguments對象,則可展開 //如果當前環境含有Symbol對象,且此變量含有Symbol.isConcatSpreadable屬性,Symbol.isConcatSpreadable用於改變array或者array-like對象使用concat時的默認行為 } export default isFlattenable
isArguments.js
import getTag from './.internal/getTag.js' import isObjectLike from './isObjectLike' /** * Checks if `value` is likely an `arguments` object. * * @since 0.1.0 * @category Lang * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an `arguments` object, else `false`. * @example * * isArguments(function() { return arguments }()) * // => true * * isArguments([1, 2, 3]) * // => false */ //判斷一個值是否是一個arguments對象 function isArguments(value) { return isObjectLike(value) && getTag(value) == '[object Arguments]' } export default isArguments
baseDifference.js
import SetCache from './SetCache.js' //創建數組的cache import arrayIncludes from './arrayIncludes.js' //arrayIncludes判斷數組是否包含給定值,參數一array,參數二value import arrayIncludesWith from './arrayIncludesWith.js' //arrayIncludesWith類似於arrayIncludes,區別是它的comparator需要作為參數傳入 import map from '../map.js' //類似於原生的map,對數組每一個元素執行迭代器后返回由返回值組成的新數組 import cacheHas from './cacheHas.js' //判斷cache中是否有給定key /** Used as the size to enable large array optimizations. */ const LARGE_ARRAY_SIZE = 200//判斷當數組參數太長時,是否開啟大數組優化 /** * The base implementation of methods like `difference` without support * for excluding multiple arrays. * * @private * @param {Array} array The array to inspect. * @param {Array} values The values to exclude. * @param {Function} [iteratee] The iteratee invoked per element. * @param {Function} [comparator] The comparator invoked per element. * @returns {Array} Returns the new array of filtered values. */ //difference,比較給定數組與其他數組,返回一個新數組,其中元素是給定數組獨特於其他數組的元素 //difference方法的基礎實現,不支持排除多個數組 //array,用於檢查的數組 //values,用於排除元素的數組 //iteratee,迭代器,類型是function,循環會調用 //comparator,比較器,類型function,循環會調用 function baseDifference(array, values, iteratee, comparator) { let includes = arrayIncludes//arrayIncludes方法,判斷數組是否包含給定值 let isCommon = true const result = []//結果數組 const valuesLength = values.length//用於排除的數組的長度 if (!array.length) {//如果array無長度,返回空數組 return result } if (iteratee) {//如果傳遞了迭代器參數,就循環values,對每個值運行迭代器,生成新的values values = map(values, (value) => iteratee(value)) } if (comparator) {//如果傳遞了比較器參數,就是用arrayIncludesWith方法 includes = arrayIncludesWith isCommon = false } else if (values.length >= LARGE_ARRAY_SIZE) { //如果values數組的長度超過200,indludes方法就換成cacheHas,啟用cache以達到性能優化的效果 includes = cacheHas isCommon = false values = new SetCache(values) } outer://label語句,將下面的for循環標記,continue會跳過本次循環,繼續走下一次外層循環 for (let value of array) {//循環array const computed = iteratee == null ? value : iteratee(value) //如果有iteratee參數,就把array數組的循環當前值value傳入iteratee,返回值存為computed value = (comparator || value !== 0) ? value : 0 if (isCommon && computed === computed) { //排除array當前循環值是否是NaN的情況,isCommon用來標記是否要使用特殊的比較方式 let valuesIndex = valuesLength//values長度 while (valuesIndex--) { //循環values,如果發現values里有值和當前array循環元素相等,直接跳出values循環,循環下一次array if (values[valuesIndex] === computed) { continue outer } } result.push(value)//values循環結束沒有發現有相等的值,就push入結果數組 } else if (!includes(values, computed, comparator)) { //如果當前array循環值是NaN或者需要使用特殊比較方法 //調用includes判斷values中有沒有和當前array循環值相等的 result.push(value) } } return result } export default baseDifference