_.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