我在lodash库中学习javascript编码技巧(difference)

本文深入解析JavaScript中的difference函数,该函数用于创建一个排除指定数组值的新数组。通过遍历和比较,利用如baseDifference、arrayIncludes等工具函数实现过滤。文章详细介绍了这些辅助函数的工作原理,涉及数组查找、类型判断等多个知识点。
摘要由CSDN通过智能技术生成

鲁迅说过:只有阅读过优秀库源码的人,才能配的上是真正的勇士。

difference

创建一个具有唯一array值的数组,每个值不包含在其他给定的数组中。(注:即创建一个新数组,这个数组中的值,为第一个数字(array 参数)排除了给定数组中的值。)该方法使用SameValueZero做相等比较。结果值的顺序是由第一个数组中的顺序确定。

换句话来说这个函数能够对比两个数组,并且以第一个参数数组为目标数组,第二个参数数组为过滤数组,最后返回过滤后的数据。

function difference(array, ...values) {
  return isArrayLikeObject(array)
    ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true))
    : []
}

difference所依赖的工具函数如下:

  1. isArrayLikeObject
  2. baseDifference
  3. isArrayLikeObject
  4. baseFlatten

1.isArrayLikeObject

该工具函数主要用来判断数组是否是对象。

isArrayLikeObject主要依赖以下函数:

function isArrayLikeObject(value) {
  return isObjectLike(value) && isArrayLike(value)
}

1.1. isObjectLike
1.2. isArrayLike

1.1 isObjectLike

该工具函数只要用来判断是否是对象和不是null的元素

function isObjectLike(value){
	return typeof ==='object' && value!==null
}

知识点:

typeof的用法:

操作符返回一个字符串,表示未经计算的操作数的类型。

typeof {} // 'object'
typeof [] // 'object'
typeof Function // 'function'
typeof null // 'object'
typeof new String(1) // 'object'
typeof String(1) // 'string'
typeof /\d+/ // 'object'
typeof undefined // 'undefined'

1.2 isArrayLike

检查value是否是数组或者是集合。一个值如果被认定为是数组或者是集合,那么它必须不是一个Funtion和有length属性,并且length属性可以等于0并且是小于Number.MAX_SAFE_INTEGER
(9007199254740991)

function isArrayLike(value) {
  return value != null && typeof value !== 'function' && isLength(value.length)
}
function isLength(value) {
  return typeof value === 'number' &&
    value > -1 && value % 1 == 0 && value <= Number.MAX_SAFE_INTEGER
}

可以看出isLength这个函数很有意思,特别是value % 1 == 0,基本上是杜绝的value是小数点的情况,而且从中也可以得知原来Function是有length属性,也算是补全了一个知识盲点。

2.baseDifference

这个函数所实现的功能给定一个待检查的数组 array,然后第二个参数则为需要排除的数组

源码如下:

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

/**
 * 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

  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

baseDifference所涉及到的函数比较多,所以只能一个一个进行解析,涉及到的函数如下:

  • arrayIncludes

其实这个函数类似与es6中,Array.prototype.includes()方法,也就是说给定一个数组和一个value值判断该value是否存在与array中。

import baseIndexOf from './baseIndexOf.js';

/**
 * 数组`includes`方法的的特殊版本,不支持指定开始搜索位置的索引。
 *
 * @private
 * @param {Array} [array] 要检查的数组
 * @param {*} target 要搜索的值
 * @returns {boolean} 如果搜索到了目标则返回true,否则返回false。
 */
function arrayIncludes(array, value) {
  // 初始化length
  const length = array == null ? 0 : array.length;
  // 当length不为0且能搜到value时,返回true,否则返回false。
  return !!length && baseIndexOf(array, value, 0) > -1;
}

export default arrayIncludes;

从上源码中也可以感觉的出来baseIndexOf使用获取value在array中的下标。

知识点:
array == null 这样的判断方式也可以判断array为undefine的情况。

  • baseIndexOf

给定一个array和一个value,返回该value在array中的下标

import baseFindIndex from './baseFindIndex.js';
import baseIsNaN from './baseIsNaN.js';
import strictIndexOf from './strictIndexOf.js';

/**
 * indexOf的基础实现,不使用fromIndex边界检查
 *
 * @private
 * @param {Array} array 要检查的数组
 * @param {*} value 要搜索的值
 * @param {number} fromIndex 搜索开始位置的索引
 * @returns {number} 返回匹配值的索引,否则返回-1
 */
function baseIndexOf(array, value, fromIndex) {
  // value为NaN的时候,调用baseFindIndex;value不为NaN的时候,调用strictIndexOf
  return value === value
    ? strictIndexOf(array, value, fromIndex)
    : baseFindIndex(array, baseIsNaN, fromIndex);
}

export default baseIndexOf;

知识点:
value === value,一般的数据类型value,都会返回true,那么这样的比较意义何在呢?后面通过翻阅mdn可以知道 NaN === NaN返回的结果是false。那么也就是说该函数考虑到value为NaN的情况。

如果value不是NaN的情况,则调用strictIndexOf()函数。

  • strictIndexOf

该函数会返回基本类型值的下标。

/**
 * indexOf的一个特定版本,对value执行严格相等比较,即 ===。
 *
 * @private
 * @param {Array} array 要检查的数组
 * @param {*} value 要搜索的值
 * @param {number} fromIndex 搜索开始位置处的索引
 * @returns {number} 返回匹配值的索引,否则返回-1
 */
function strictIndexOf(array, value, fromIndex) {
  // 把fromIndex位置处的值包含进来
  let index = fromIndex - 1;
  // 取到array.length
  const { length } = array;

  // 迭代
  while (++index < length) {
    // 比较时使用===
    if (array[index] === value) {
      // 返回搜索到的索引
      return index;
    }
  }
  // 返回-1
  return -1;
}

export default strictIndexOf;

我对于fromIndex这个参数是实属有点难理解,为什么需要添加这个参数呢?难道有场景不是从0开始需要元素的吗?

如果value是NaN则会调用baseFindIndex()函数

  • baseFindIndex

baseIsNaN

/**
 * isNaN的实现基础,不支持对象形式的数字。
 *
 * @private
 * @param {*} value 要检查的值
 * @returns {boolean} 如果value是NaN则返回true,否则返回false。
 */
function baseIsNaN(value) {
  // 只有NaN !== NaN
  return value !== value;
}

export default baseIsNaN;
/**
 * `findIndex` 和 `findLastIndex`的实现基础
 *
 * @private
 * @param {Array} array 打算检索的数组
 * @param {Function} predicate 每次迭代时调用的函数
 * @param {number} fromIndex 起始处的索引位置
 * @param {boolean} [fromRight] 指定是否从右向左迭代
 * @returns {number} 返回找到元素的 索引值(index),否则返回 -1。
 */
function baseFindIndex(array, predicate, fromIndex, fromRight) {
  // 取到array.length
  const { length } = array;
  // 如果从左向右,起始索引位置就是fromIndex - 1;如果从右向左,起始索引位置是 fromIndex + 1。
  // 目的是迭代时能把fromIndex包进去。
  let index = fromIndex + (fromRight ? 1 : -1);

  // 迭代时,从左向右则index递增,从右向左则index递减
  while (fromRight ? index-- : ++index < length) {
    // 在这里定义了predicate函数的参数(item, index, array),找到了想要的元素则返回true,predicate函数指的是baseIsNaN(),虽然传递了三个参数,但是baseIsNaN只接受第一个参数。
    if (predicate(array[index], index, array)) {
      // 返回想寻找元素的索引
      return index;
    }
  }
  // 所有的元素都不匹配,就返回 -1。
  return -1;
}

export default baseFindIndex;

知识点:
我发现lodash很喜欢通过函数传递函数这样的方式来进行操作,或许这样更利于业务函数的扩展吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值