【lodash】difference源码研读解析

2023-05-16

A modern JavaScript utility library delivering modularity, performance & extras.

lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库

一、环境准备

  • lodash 版本 v4.0.0

  • 通过 github1s 网页可以 查看 lodash - difference 源码

  • 调试测试用例可以 clone 到本地

git clone https://github.com/lodash/lodash.git

cd axios

npm install

npm run test

二、结构分析

difference_relation.jpg

  这是一张 difference 依赖引用路径图,相对复杂一些,按照功能划分,大致包括 cache 模块、 index 模块和 flatten 模块。接下来会自底向上分析各个依赖模块。由于依赖较多,篇幅较长,将按照模块分成四个部分,本篇主要讲述 difference 主体模块,包含 isArrayLikeisObjectLikeisArrayLikeObjectarrayIncludesWithmapcacheHasbaseDifferencedifference

三、函数研读

1. isArrayLike 模块

**检查 value 是否与数组类似。值被视为数组,它不是函数并且有一个 value.length ,这是一个大于等于’0’且小于 MAX_SAFE_INTEGERNumber **

import isLength from './isLength.js'
/**
 * @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

  • 重点关注 isLength,判断规则是 typeof value === 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER,其中 value % 1 == 0 确保 value 是整数,MAX_SAFE_INTEGER = 9007199254740991

2. isObjectLike 模块

检查“value”是否与对象类似,如果不为空则是一个对象,并且会有一个“typeof”运算结果为“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) {
  return typeof value === 'object' && value !== null
}

export default isObjectLike

  • 可以通过 typeof 来获取 未经计算的操作数 的类型,下面是一个 typeof 运算结果集
类型结果
Undefined“undefined”
Null“object”
Boolean“boolean”
Number“number”
BigInt(ECMAScript 2020 新增)“bigint”
String“string”
Symbol (ECMAScript 2015 新增)“symbol”
宿主对象(由 JS 环境提供)取决于具体实现
Function 对象 (按照 ECMA-262 规范实现 [[Call]])“function”
其他任何对象“object”

3. isArrayLikeObject 模块

此方法类似于 isArrayLike,只是它还检查 value 是一个 Object

import isArrayLike from './isArrayLike.js'
import isObjectLike from './isObjectLike.js'
/**
 * @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

  • 封装了 isObjectLikeisArrayLike,当 value 同时符合两者所检测的目标类型时返回 true,否则返回 false

4. arrayIncludesWith 模块

这个函数类似于 arrayIncludes,只是它接受一个比较器(comparator)

/**
 * @private
 * @param {Array} [array] The array to inspect.
 * @param {*} target The value to search for.
 * @param {Function} comparator The comparator invoked per element.
 * @returns {boolean} Returns `true` if `target` is found, else `false`.
 */
function arrayIncludesWith(array, target, comparator) {
  if (array == null) {
    return false
  }

  for (const value of array) {
    if (comparator(target, value)) {
      return true
    }
  }
  return false
}

export default arrayIncludesWith

  • 如果待搜索数组 arraynull,直接返回 false
  • 使用 for...of 迭代待搜索数组 array 中的每一项,使用 if 判断比较器 comparator(target, value) 的返回值并给出对应返回结果

5. map 模块

通过 iteratee 运行 array 的每个元素来创建一个数组 resultiteratee 由三个参数调用:(value, index, array)。

/**
 * @since 5.0.0
 * @category Array
 * @param {Array} array The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the new mapped array.
 * @example
 *
 * function square(n) {
 *   return n * n
 * }
 *
 * map([4, 8], square)
 * // => [16, 64]
 */
function map(array, iteratee) {
  let index = -1
  const length = array == null ? 0 : array.length
  const result = new Array(length)

  while (++index < length) {
    result[index] = iteratee(array[index], index, array)
  }
  return result
}

export default map

  • 使用 new Array 创建一个对应其长度的数组 result,其中 arraynull 时,长度为 0,将会创建一个空数组
  • 按照 array 长度循环调用 iteratee,每次循环步长 + 1

6. cacheHas 模块

检查 keycache 值是否存在

/**
 * @private
 * @param {Object} cache The cache to query.
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function cacheHas(cache, key) {
  return cache.has(key)
}

export default cacheHas
  • cachekey 均为入参

7. baseDifference 模块

difference 这样的方法的基本实现,不支持排除多个数组

import SetCache from './SetCache.js'
import arrayIncludes from './arrayIncludes.js'
import arrayIncludesWith from './arrayIncludesWith.js'
import map from '../map.js'
import cacheHas from './cacheHas.js'

/** Used as the size to enable large array optimizations. */
const LARGE_ARRAY_SIZE = 200

/**
 * @private
 * @param {Array} array The array to inspect.
 * @param {Array} values The values to exclude.
 * @param {Function} [iteratee] 每个元素调用的迭代对象
 * @param {Function} [comparator] 每个元素调用的比较器
 * @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

  if (!array.length) {
    return result
  }
  if (iteratee) {
    values = map(values, (value) => iteratee(value))
  }
  if (comparator) {
    includes = arrayIncludesWith
    isCommon = false
  } else if (values.length >= LARGE_ARRAY_SIZE) {
    includes = cacheHas
    isCommon = false
    values = new SetCache(values)
  }
  outer:
  for (let value of array) {
    const computed = iteratee == null ? value : iteratee(value)

    value = (comparator || value !== 0) ? value : 0
    if (isCommon && computed === computed) {
      let valuesIndex = valuesLength
      while (valuesIndex--) {
        if (values[valuesIndex] === computed) {
          continue outer
        }
      }
      result.push(value)
    } else if (!includes(values, computed, comparator)) {
      result.push(value)
    }
  }
  return result
}

export default baseDifference
  • 要检查的 array 为空(array.length = 0),直接返回空数组
  • 迭代器 iteratee 存在,则在 map 内完成对待排除内容数组 values 每一项元素的迭代,返回一个符合迭代器规则的待排除数组 values
  • 比较器 comparator 存在,则设定排除方法 includes = arrayIncludesWith,若待排除内容过大 values.length >= LARGE_ARRAY_SIZE = 200 则不宜使用数组间比较,而是使用 cache 中的 map 做存储比较(values = new SetCache(values)),这样虽然牺牲了空间,但可以用 map 操作时间短的优势弥补,典型的牺牲空间换时间策略 🐶
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【lodash】difference源码研读解析 的相关文章

  • 使用 Lodash 删除数组中的元素

    我有这个数组 var fruits Apple Banana Orange Celery 我用Lodash的remove像这样 remove fruits function fruit return fruit Apple Banana O
  • 使用 LoDash 合并包含相同键/值的对象

    在对初始数组中的 LoDash 进行一些操作后 我最终得到以下结果 number 3 product apple number 2 product apple number 4 product pear 我怎样才能操纵它最终得到 numbe
  • 根据值过滤对象属性

    是否有一些优雅的方法可以使用 lodash 下划线从该对象中过滤掉错误属性 类似于如何 compact array 从数组中删除错误元素 so from propA true propB true propC false propD tru
  • 使用 lodash 对 Typescript 中的项目进行分组

    我需要按日期对对象集合进行分组 var meetings date 2001 01 01 13 00 place park date 2001 01 01 14 00 place school date 2001 01 02 11 00 p
  • Oracle JDK 和 OpenJDK 之间的区别

    注意 这个问题来自 2014 年 从 Java 11 OpenJDK 和 Oracle 开始 JDK 正在趋同 Oracle 和 OpenJDK 之间有什么重要区别吗 例如 垃圾收集和其他 JVM 参数是否相同 两者之间的 GC 工作方式是
  • 如何通过Lodash将对象转换为排序数组

    如何转型 2 b 3 c 1 a into 1 a 2 b 3 c 通过洛达什 使用起来相当简单Object keys Array map 你真的不需要 lodash const obj 2 b 3 c 1 a const arr Obje
  • 嵌套集合的 Lodash 映射

    我有以下收藏 var realty name Realty A entrances name Entrance A units name unitA contracts contractNo no 963 contractNo no 741
  • Mockito Matchers isA、any、eq 和 Same 之间有什么区别?

    我对它们之间的区别以及在哪种情况下选择哪一个感到困惑 有些差异可能很明显 例如any and eq 但我将它们全部包括在内只是为了确定 我想知道它们的差异 因为我遇到了这个问题 我在 Controller 类中有这个 POST 方法 pub
  • 查找嵌套 json 对象深处的对象

    我在下面的代码片段中有嵌套的 json 对象 想要查找所有出现的 schema 并将包含该架构值的整个对象保存到另一个对象中 我尝试使用 lodash 过滤器 但没有成功 有没有人有什么建议 element parseResult cont
  • WPF 中 DataTemplate 中的 x:Key、x:Name 和 x:UID 有什么区别?

    我正在尝试在 WPF 中创建动态选项卡 并且正在尝试编写一个仅适用于某些选项卡项目的内容模板 我希望能够为内容模板创建一个标识符 以便我可以在后面的代码中引用它 这样我就可以有选择地将它应用于单个 TabControl 中的某些选项卡 但是
  • 使用 lodash 动态计算嵌套集合的平均值

    我有一个 JSON 对象数组 集合 例如 x x1 1 y yt 0 zt 4 qa 3 ft 0 x x1 5 y yt 10 zt 2 qa 0 ft 0 我想计算每个字段的平均值 结果结构应该相同 喜欢 x x1 3 y yt 5 z
  • 如何合并对象数组?

    假设我有一系列文章 每篇文章可能有也可能没有超过 1 个图像对象 现在由于 mysql 无法将对象分组在一起 所以你必须自己做 所以结果是你得到near重复的文章对象 唯一的区别是图像对象 By near重复我的意思是返回结果的唯一区别是图
  • 如何查找两个 JavaScript 对象数组之间的差异?

    我有两个 JavaScript 数组orig 原始对象数组 和update 更新后的对象原始数组 具有相同的长度并包含对象 我想输出每对对象之间的差异 Example var orig enabled true name Obj1 id 3
  • Angular 2 - 消除 keyUp 事件的抖动

    如何消除在 keyUp 事件上调用的函数 这是我的代码 我的功能 private handleSearch searchTextValue string skip number void this searchTextValue searc
  • Angular 4 HttpClient 查询参数

    我一直在寻找一种将查询参数传递到 API 调用中的方法HttpClientModule s HttpClient并尚未找到解决方案 与旧的Http模块你会写这样的东西 getNamespaceLogs logNamespace Setup
  • SupportMapFragment 与 MapFragment 性能对比

    之间有什么区别吗支持MapFragment http developer android com reference com google android gms maps SupportMapFragment html and 地图片段
  • 如何让 lodash 与 Angular JS 一起工作?

    我正在尝试使用 lodash 使用它ng repeat指令 以这种方式 div div Hello n div div Being GridController IndexModule controller GridController f
  • 将包含对象的数组压平为 1 个对象

    给定输入 a 1 b 2 c 3 如何返回 a 1 b 2 c 3 对于数组这不是一个问题 http underscorejs org flatten使用 lodash 但这里我们有对象数组 Use Object assign https
  • Lodash 和 Underscore.js 之间的差异 [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 为什么有人会更喜欢Lodash http lodash com or 下划线 js http underscorejs org 实用程序库优于其
  • 使用 Lodash 循环 JavaScript 对象中的属性

    是否可以循环访问 JavaScript 对象中的属性 例如 我有一个 JavaScript 对象定义如下 myObject options property1 value 1 property2 value 2 属性将动态添加到该对象 有没

随机推荐