# baseUniq
# Description
baseUniq 的作用是数组去重,是实现 uniq 、uniqBy 和 uniqWith 的内部方法,因此除了需要支持基本的去重操作外,还要支持 uniqBy 的 iteratee 参数和 uniqWith 的 comparator 参数。
# Params
(array, iteratee, comparator)
# Return
Array
# Depend
import SetCache from './SetCache.js'
import arrayIncludes from './arrayIncludes.js'
import arrayIncludesWith from './arrayIncludesWith.js'
import cacheHas from './cacheHas.js'
import createSet from './createSet.js'
import setToArray from './setToArray.js'
SetCache 源码分析
arrayIncludes 源码分析
arrayIncludesWith 源码分析
cacheHas 源码分析
createSet 源码分析
setToArray 源码分析
# Code
/** Used as the size to enable large array optimizations. */
const LARGE_ARRAY_SIZE = 200
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
includes = arrayIncludesWith
}
else if (length >= LARGE_ARRAY_SIZE) {
const set = iteratee ? null : createSet(array)
if (set) {
return setToArray(set)
}
isCommon = false
includes = cacheHas
seen = new SetCache
}
else {
seen = iteratee ? [] : result
}
outer:
while (++index < length) {
let value = array[index]
const computed = iteratee ? iteratee(value) : value
value = (comparator || value !== 0) ? value : 0
if (isCommon && computed === computed) {
let seenIndex = seen.length
while (seenIndex--) {
if (seen[seenIndex] === computed) {
continue outer
}
}
if (iteratee) {
seen.push(computed)
}
result.push(value)
}
else if (!includes(seen, computed, comparator)) {
if (seen !== result) {
seen.push(computed)
}
result.push(value)
}
}
return result
}
# Analyze
首先定义了一系列的变量及对参数等处理
- 定义 includes
- 定义 isCommon
- 定义结果数组 result
- 拿到 array.length
判断了是否传入了
comparator,如果传入了isCommon为false,includes为arrayIncludesWith如果没有传入
comparator,那么会判断数组的长度是不是大于等于200if (comparator) { // ... } else if (length >= LARGE_ARRAY_SIZE) { const set = iteratee ? null : createSet(array) if (set) { return setToArray(set) } isCommon = false includes = cacheHas seen = new SetCache }可以看到,如果没有传入 自定义处理函数
iteratee时,会使用Set来对数组进行去重,会判断如果set为真值时,会调用setToArray返回结果否则会将
isCommon置为false,并且将seen设置为new SetCache,对于大数组使用缓存处理,提升性能,这里同时也会将includes改变如果既没有传入
comparator,并且数组的长度也没有大于199,那么会判断是否传入了iterateeif (comparator) { // ... } else if (length >= LARGE_ARRAY_SIZE) { // ... } else { seen = iteratee ? [] : result }这里会根据是否传入了
iteratee来决定seen的值while 循环来进行元素比较,首先看元素值不是 NaN ,并且 isCommon 为 ture 的情况
outer: while (++index < length) { let value = array[index] const computed = iteratee ? iteratee(value) : value value = (comparator || value !== 0) ? value : 0 if (isCommon && computed === computed) { let seenIndex = seen.length while (seenIndex--) { if (seen[seenIndex] === computed) { continue outer } } if (iteratee) { seen.push(computed) } result.push(value) } }可以看到首先拿到当前遍历的元素,然后会判断 是否传入了
iteratee函数,也就是定义了computed, 如果传入了iteratee,则使用iteratee处理过之后的值进行比较,否则还取value对于
value也会单独做一次处理,会将+0和-0转为 0, 但是如果传入了comparator函数,这一点就会交给comparator函数处理接着就是判断逻辑了,
while循环,拿到seen的每一项和computed进行对比,如果找到相同的,则跳出外层循环,进行下一个值的迭代,因为当前值已经重复了如果 while 循环完成后,没有重复,则会判断 是否传入了 iteratee 函数。
可以结合之前的逻辑看到,如果没有传入
iteratee函数,seen和result指向同一内存空间,所以,如果没有传入,则使用result进行push即可,如果传入了 则seen和result都要进行push,毕竟判断的时候 使用的是seen接着是处理
computed为NaN和isCommon为false的情况else if (!includes(seen, computed, comparator)) { if (seen !== result) { seen.push(computed) } result.push(value) }这一块可以分这么几步来看
computed为NaN, 那到这里会使用arrayIncludes或者arrayIncludesWith来进行判断,如果不存在的话,则会判断seen和result是否指向了同一内存空间,如果指向同一内存空间,则只需要resultpush即可,否则seen和result都要push- 传入了
comparator函数,在传入了comparator函数时,isCommon也为false,所以这个时候 这里的includes函数其实就是arrayIncludesWith - 没有传入
comparator函数,但是数组的长度超过了199, 并且 没有传入iteratee函数时,这里会使用SetCache来缓存数组,也就是includes函数在这时,只需要seen和computed两个参数,并且 会将isCommon置为false,也就是 如果满足本条叙述,在迭代时也会一直走这个分支
在没有传入
iteratee函数时,seen和result指向内存空间一致, 在while循环中computed和value也是一样的,所以 根本不需要seen去pushcomputed,但是 如果传入了,那么比较时,使用的是处理之后的值,而此时seen和result也不指向同一内存空间,所以就需要seen来pushcomputed用作后续的比较
# Remark
Set MDN (opens new window) 对象允许你存储任何类型的唯一值,无论是原始值或者是对象引用。
Set 对象是值的集合,你可以按照插入的顺序迭代它的元素。 Set 中的元素只会出现一次,即 Set 中的元素是唯一的。
# Example
const a = {a: 1}
console.log(baseUniq([a, a, 1, 1, 1, 1, 2, 3, 3, 4])) // [ { a: 1 }, 1, 2, 3, 4 ]