flatten
flatten
是用來實現數組扁平化的,并加入了shallow
函數strict
來表示是否只將數組進行一次扁平化和是否對參數有嚴格要求。
然而我覺得官方的實現在效率上是有問題的。忽略shallow
和strict
,單看flatten本身的實現。
var flatten = function (input, shallow, strict, startIndex) {
var output = [],
idx = 0;
for (var i = startIndex || 0, length = input && input.length; i < length; i++) {
var value = input[i];
if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
//flatten current level of array or arguments object
if (!shallow) value = flatten(value, shallow, strict);
var j = 0,
len = value.length;
output.length += len;
while (j < len) {
output[idx++] = value[j++];
}
} else if (!strict) {
output[idx++] = value;
}
}
return output;
};
underscore源碼的實現中,對于嵌套數組中的元素,會進入下一層flatten函數調用;在這層調用中,所有數組會被推入output并返回上一層(output[idx++] = value;
);返回上一層后,又會遍歷一遍這個數組(while (j < len) output[idx++] = value[j++]
)。
也就是說,這相當于將嵌套數組中的元素遍歷了兩遍。
然而這種遍歷兩次并不是不能避免的。
// 忽略startIndex的實現
var flatten = function (input, shallow) {
var res = [];
(function fn(res, array, deep) {
array.forEach(item => {
if (isArrayLike(item) && (_.isArray(item) || _.isArguments(item))) {
if (shallow && deep) {
res.push(item);
} else {
fn(res, item, true);
}
} else {
res.push(item);
}
});
})(res, input, false);
return res;
}
我們將最終返回結果的引用res
在整個flatten
調用棧中都調用,凡是遇見非數組元素,就將這個元素push
到res
中;遇到數組,也只是進入下一層調用中,將所有非數組元素推進去。這樣就可以避免非數組元素被遍歷兩次了。
2018.11.27更新:
Underscore源碼沒有問題……今早睡醒突然在想flatten
函數,發現我的遍歷是不能滿足strict
參數的……源碼應該是為了滿足strict
參數所以才遍歷了兩遍,沒毛病。