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
参数所以才遍历了两遍,没毛病。
网友评论