_.difference(array, [values])


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

 


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM