美文网首页
lodash源码阅读 —— difference

lodash源码阅读 —— difference

作者: 就想叫菜鸟 | 来源:发表于2018-07-08 17:46 被阅读0次

    lodash 源码阅读 —— difference

    • 该方法是用于过滤,第一个参数是需要过滤的数组,第二个参数是需要被过滤掉(移除)的元素集合,该方法返回的传入的第一个参数的子集或者自身。

    • 用法:_.difference(value, [values]);

    • lodash源码仓库

    • 简单分析:如果写为 _.difference(A, B) 就相当于求 A-B,即 A 和 B 的差集,也叫做相对补集,就是属于 A 但是不属于 B 的元素。

    1. 下面是源码,然后我将依次贴出内部的工具方法

      /**
       * 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) {
        return isArrayLikeObject(array)
          ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
          : []
      }
      
      export default difference
      
    2. isArrayLikeObject 方法

      /**
       * 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
       */
      function isArrayLikeObject(value) {
        return isObjectLike(value) && isArrayLike(value)
      }
      
      export default isArrayLikeObject
      
      • 该方法用于判断传入的值是否是一个数组或者类数组结构,如果是就返回 true,那这里的 isObjectLike()isArrayLike() 是怎么实现的呢,下面我将贴出源码。
    3. isObjectLike()isArrayLike()

      /**
       * 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
       */
      function isObjectLike(value) {
          // 值不为空,并且 typeof 返回值是 object
        return typeof value == 'object' && value !== null
      }
      
      export default isObjectLike
      
      /**
       * 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
       */
      function isArrayLike(value) {
        return value != null && typeof value != 'function' && isLength(value.length)
      }
      
      export default isArrayLike
      
      • 这里的 isObjectLike 判断传入的参数是不是一个对象或者类对象,isArrayLike 判断传入的参数是不是一个数组或者类数组,然后你会发现又跑出来一个 isLength 这个函数很简单,有兴趣的可以去翻下源码,就是为了判断长度是不是一个有限的数字。
    4. baseDifference: 重头戏部分

      /** 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.
       */
      function baseDifference(array, values, iteratee, comparator) {
        let includes = arrayIncludes
        let isCommon = true
        const result = []
        const valuesLength = values.length
      
        // 判断如果数组的长度是 0,则返回空数组
        if (!array.length) {
          return result
        }
        // 如果传入 iteratee 函数,则遍历处理 values 的每一个值。
        if (iteratee) {
          values = map(values, (value) => iteratee(value))
        }
        if (comparator) {
          includes = arrayIncludesWith
          isCommon = false
        }
        // 当数组的长度大于 200 时执行,这是前面给出的常量。
        else if (values.length >= LARGE_ARRAY_SIZE) {
          // 这里的 cacheHas 是一个函数,有兴趣的可以去查源码,这里就不多余讲了。
          includes = cacheHas
          isCommon = false
          values = new SetCache(values)
        }
        // 标签语法
        outer:
        for (let value of array) {
          // 当 iteratee == null 时返回 value,否则返回 iteratee(value)
          const computed = iteratee == null ? value : iteratee(value)
          // 当 comparator 存在或者 value !==0 时,value = value
          value = (comparator || value !== 0) ? value : 0
          if (isCommon && computed === computed) {
              // values 的长度
            let valuesIndex = valuesLength 
            while (valuesIndex--) {
              if (values[valuesIndex] === computed) {
               // 如果 values 中有值等于 computed,就退出到最外层的循环,这就是标签语法的作用。
                continue outer
              }
            }
            result.push(value)
          }
          else if (!includes(values, computed, comparator)) {
            result.push(value)
          }
        }
        return result
      }
      
      export default baseDifference
      
      // 下面来举个例子讲一下 baseDifference([1, 2], [2, 3, 4]) 这里 array = [1, 2] , values = [2, 3, 4]
      // 初始值 result = [], valuesLength = 3
      // for 遍历,第一个值 value = 1, computed = 1, valuesIndex = 3;
      // values[2] = 4 !== value, values[1] = 3 !== value, values[0] = 2 !== value
      // result = [1]
      // 接下来,遍历第二个值, value = 2, computed = 2, vlauesIndex = 3;
      // values[2] = 4 !== value, values[1] = 3 !== value, values[0] = 2 == value
      // 当存在值相等的时候,直接跳转到最外层,所以 result = [1]
      // baseDifference([1, 2], [2, 3, 4]) = [1];
      
      • 看到 outer:是不是一下子懵了,这就是标签语法,形式是 label: statement,这里的标签可以是除了保留字以外的任意标识符,当然语句也可以是任意语句。它有什么用呢?这个标签就相当于一个定位符,在 break, continue 后面用于指定跳转的位置。
    5. baseFlatten:关于这部分的源码可以去看我上一篇文章:Lodash 源码阅读 —— concat

    • 本人菜鸟,只是一些拙见,有什么错误请大家帮忙指出。

    相关文章

      网友评论

          本文标题:lodash源码阅读 —— difference

          本文链接:https://www.haomeiwen.com/subject/qcwouftx.html