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方法可以消去数组中的指定元素,而它的源码也主要是通过遍历来实现的。