lodash源码分析之basePullAll

本文为读 lodash 源码的第六十篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash

gitbook也会同步仓库的更新,gitbook地址:pocket-lodash

依赖

import baseIndexOf from './baseIndexOf.js'
import baseIndexOfWith from './baseIndexOfWith.js'
import copyArray from './copyArray.js'

《lodash源码分析之baseIndexOf》

《lodash源码分析之baseIndexOfWith》

《lodash源码分析之copyArray》

源码分析

basePullAll 会被 pullAllpullpullBypullWith 等函数调用,是实现这些函数的基础。

它的作用是将数组 array 中存在于数组 values 中的元素移除。同时也支持迭代器 iteratee 来用返回新的比较值,也支持 comparator 自定义比较函数。

源码如下:

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
}

indexOf的选择

const indexOf = comparator ? baseIndexOfWith : baseIndexOf

indexOf 的作用和数组原生的 indexOf 方法类似,返回某个值在数组中的索引,如果值不在数组中,则返回 -1,因此也可以用来检测某个值是否在数组中。

这里,如果有传自定义比较器,则使用 baseIndexOfWith 函数,因为 baseIndexOfWith 支持自定义的比较器来比较。

copy values

if (array === values) {
  values = copyArray(values)
}

为什么要在 arrayvalues 一致时要将 values 复制一份呢,因为数组是一个引用类型,后续会对 array 做一些可变操作,如果不对 values 复制,则 values 也会跟着 array 一起变动,会造成循环时出错。

迭代器的使用

if (iteratee) {
  seen = map(array, (value) => iteratee(value))
}

如果有传迭代器,表示调用方可能想使用新值来进行比较,这里使用 iteratee 返回的值组成新的数组 seen

执行移除操作

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)
  }
}

我们先将代码简化一下,就比较好理解了。

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

  while ((fromIndex = indexOf(array, value, fromIndex)) > -1) {
    array.splice(fromIndex, 1)
  }
}

以上代码是逻辑不严谨的,但是可以很清楚地看到移除的逻辑,其实就是遍历 values ,然后用 indexOf 方法检测 array 中是否存在存在于 values 中的值,直至将存在于 values 中的值都删除完毕。

至于为什么会有 computed,是因为使用方可以传 iteratee 返回新的比较值。

if (seen !== array) {
  seen.splice(fromIndex, 1)
}

也由于有 iteratee 的存在,真正比较的可能不是 array ,因此 indexOf 传入的是 seen 而不是 array ,如果seenarray 并不相同,在 array 执行移除操作时,seen 也要执行移除操作。

License

署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)

最后,所有文章都会同步发送到微信公众号上,欢迎关注,欢迎提意见:

作者:对角另一面

results matching ""

    No results matching ""