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