lodash源码阅读5-----------uniq

用法

数组去重

uniq([2, 1, 2])
 // => [2, 1]

解析

首先来看它的函数入口,没什么说的,就是判断数组是否有意义,有的话调用baseUniq方法

function uniq(array) {
  return (array != null && array.length)
    ? baseUniq(array)
    : []
}

接下来看看,baseUniq方法

首先会做一个类型的判断,判断是否使用常规的遍历,还是使用Set去重,或直接构造setCache。

function baseUniq(array, iteratee, comparator) {
  let index = -1
  let includes = arrayIncludes
  let isCommon = true

  const { length } = array
  const result = []
  let seen = result

  if (comparator) {
    isCommon = false
    //arrayIncludesWith方法通过传入的比较器来比较
    includes = arrayIncludesWith
  }
  else if (length >= LARGE_ARRAY_SIZE) {
      //如果传了遍历器,就构造成缓存对象
      //如果没有传遍历器,就构造成set对象
	//这里直接通过set对象的特性,完成数组去重
    const set = iteratee ? null : createSet(array)
    if (set) {
      return setToArray(set)
    }
    isCommon = false
    includes = cacheHas
    seen = new SetCache
  }
  else {
      //seen构造成新数组,或者指向result数组
    seen = iteratee ? [] : result
  }

然后我们来看看遍历部分的代码,大概的逻辑就是判断新数组中是否已经有了该元素,这里主要注意的就是NaN的判断

outer:
  while (++index < length) {
    let value = array[index]
    const computed = iteratee ? iteratee(value) : value

    value = (comparator || value !== 0) ? value : 0
    //判断computed === computed  是排除NaN的情况
    if (isCommon && computed === computed) {
      let seenIndex = seen.length
      while (seenIndex--) {
          //遍历比较,如果有相同的则跳出
        if (seen[seenIndex] === computed) {
          continue outer
        }
      }
      //iteratee存在就把计算属性放进去
      if (iteratee) {
        seen.push(computed)
      }
      result.push(value)
    }
    //如果用到了set或者setCache,就用includes方法判断
    else if (!includes(seen, computed, comparator)) {
      if (seen !== result) {
        seen.push(computed)
      }
      result.push(value)
    }
  }
  return result
}

然后来看看他的includes方法

//判断数组是否有意义
function arrayIncludes(array, value) {
  const length = array == null ? 0 : array.length
  return !!length && baseIndexOf(array, value, 0) > -1
}

//判断要查找的元素是否为NaN
function baseIndexOf(array, value, fromIndex) {
  return value === value
    ? strictIndexOf(array, value, fromIndex)
    : baseFindIndex(array, baseIsNaN, fromIndex)
}
//遍历比较数值
function strictIndexOf(array, value, fromIndex) {
  let index = fromIndex - 1
  const { length } = array

  while (++index < length) {
    if (array[index] === value) {
      return index
    }
  }
  return -1
}

//根据传入的遍历条件来判断,这里的是判断NaN
function baseFindIndex(array, predicate, fromIndex, fromRight) {
  const { length } = array
  let index = fromIndex + (fromRight ? 1 : -1)

  while ((fromRight ? index-- : ++index < length)) {
    if (predicate(array[index], index, array)) {
      return index
    }
  }
  return -1
}

最后来看看构造set对象的这块代码,setToArray很好理解,就是遍历生成数组,但在createSet函数中,做了一次判断,开始我也无法理解,后面在segmentfaultk看到了一个回答。

const set = iteratee ? null : createSet(array)
if (set) {
  return setToArray(set)
}

const createSet = (Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY)
? (values) => new Set(values)
: () => {}

function setToArray(set) {
  let index = -1
  const result = new Array(set.size)

  set.forEach((value) => {
    result[++index] = value
  })
  return result
}

对于 Set 来说, -0 和 +0 是有区别的, 但在 es2015 后规范指定 -0 和 +0 相等. 存在浏览器兼容问题. 如果是遵循标准的,则 new Set([,-0]))[1] 返回 0, 否则会返回 -0, 1 / -0 的结果为 -Infinity 不等于 Infinity, 此时使用空对象来作为标准 Set 的替代实现

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值