lodash源码阅读—–用于过滤的方法pull

吐槽君 分类:javascript

用法

pull方法可以接收多个参数,第一个参数为目标数组,后面的参数为需要除去的元素。

  const array = ['a', 'b', 'c', 'a', 'b', 'c']
 
  pull(array, 'a', 'c')
  console.log(array)
  //=> ['b', 'b']
 
 

lodash还有一个pullAll方法,用法几乎都是一样的,不同的只是它接收两个参数,第一个是目标数组,第二参数也是一个需要除去元素构成的数组。

 const array = ['a', 'b', 'c', 'a', 'b', 'c']
 
 pullAll(array, ['a', 'c'])
 console.log(array)
 // => ['b', 'b']
 

解析

由于pull方法是直接通过调用pullAll方法实现的, 这里我们直接看pullAll方法.

先来看入口函数。

function pullAll(array, values) {
  return (array != null && array.length && values != null && values.length)
    ? basePullAll(array, values)
    : array
}
 

方法很简单,就是判定我们传入的目标数组,和出去元素数组是否为空,如果不为空,调用basePullAll方法,接下来我们直接来看basePullAll方法。

function basePullAll(array, values, iteratee, comparator) {
  const indexOf = comparator ? baseIndexOfWith : baseIndexOf
  const length = values.length

  let index = -1
  let seen = array

  if (array === values) {
    values = copyArray(values)
  }
  if (iteratee) {
    seen = map(array, (value) => iteratee(value))
  }
  while (++index < length) {
    let fromIndex = 0
    const value = values[index]
    const computed = iteratee ? iteratee(value) : value

    while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
      if (seen !== array) {
        seen.splice(fromIndex, 1)
      }
      array.splice(fromIndex, 1)
    }
  }
  return array
}
 

然后我们来看看这里,这里的意思是,当array和values指向同一个数组的时候,需要克隆这个数组。
而copyArray方法就是对目标数组进行循环遍历赋值。

  if (array === values) {
    values = copyArray(values)
  }
  
  function copyArray(source, array) {
  let index = -1
  const length = source.length

  array || (array = new Array(length))
  while (++index < length) {
    array[index] = source[index]
  }
  return array
}
 

由于我们只传入了两个参数,所以函数中的很多步骤我们都可以忽略,重点来看一下这个循环。
这里length表示过滤数组的长度,seen是迭代后的数组(我们没有传入遍历器,所以本身还是原数组)

while循环是通过除去元素集合来进行的循环,这里调用iteratee方法的原因是,如果对目标数组进行了遍历,那么也需要对除去元素集合进行遍历。

之后再通过indexof方法判定目标元素是否存在,如果不存在会直接跳出循环,如果存在的话,就通过splice方法在原数组上进行消去,splice方法接收前两个参数表示(1. 删除元素的位置,2.删除元素的个数),消除后不会马上退出循环,由于indexOf是返回第一个匹配元素的位置,所以如果还存在相同元素,则会继续执行while循环,知道把同一个元素重复消去。

while (++index < length) {
    let fromIndex = 0
    const value = values[index]
    const computed = iteratee ? iteratee(value) : value

    while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
      if (seen !== array) {
        seen.splice(fromIndex, 1)
      }
      array.splice(fromIndex, 1)
    }
  }
 

然后我们来看看他的两个indexOf方法

  • baseIndexOfWith的话,是需要自己传入比较器,然后返回符合要求元素的序号。
  • baseIndexOf的话,先通过自身比较判定了value是不是NaN,而strictIndexOf,baseFindIndex都是通过遍历数组进行比较,后者则是考虑了NaN的情况。
function baseIndexOfWith(array, value, fromIndex, comparator) {
  let index = fromIndex - 1
  const { length } = array

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

function baseIndexOf(array, value, fromIndex) {
  return value === value
    ? strictIndexOf(array, value, fromIndex)
    : baseFindIndex(array, baseIsNaN, fromIndex)
}
 

总结

pull和pullAll方法可以消去数组中的指定元素,而它的源码也主要是通过遍历来实现的。


如果你觉得对你有用的话,不妨留个赞呀~~~^-^

回复

我来回复
  • 暂无回复内容