上一篇文章 「前端面试题系列8」数组去重(10 种浓缩版) 的最后,简单介绍了 lodash 中的数组去重方法 _.uniq
,它可以实现我们日常工作中的去重需求,能够去重 NaN
,并保留 {...}
。
今天要讲的,是我从 _.uniq 的源码实现文件 baseUniq.js 中学到的几个很基础,却又容易被忽略的知识点。
三个 API
让我们先从三个功能相近的 API 讲起,他们分别是:_.uniq
、_.uniqBy
、_.uniqWith
。它们三个背后的实现文件,都指向了 .internal 下的 baseUniq.js。
区别在于 _.uniq 只需传入一个源数组 array, _.uniqBy 相较于 _.uniq 要多传一个迭代器 iteratee
,而 _.uniqWith 要多传一个比较器 comparator
。iteratee 和 comparator 的用法,会在后面说到。
以 _.uniqWith 为例,它是这样调用 _.baseUniq 的:
function uniqWith(array, comparator) {
comparator = typeof comparator == 'function' ? comparator : undefined
return (array != null && array.length)
? baseUniq(array, undefined, comparator)
: []
}
baseUniq 的实现原理
baseUniq 的源码并不多,但比较绕。先贴一下的源码。
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[seenI