Underscore源码阅读:flatten

flatten

flatten是用来实现数组扁平化的,并加入了shallow函数strict来表示是否只将数组进行一次扁平化和是否对参数有严格要求。

然而我觉得官方的实现在效率上是有问题的。忽略shallowstrict,单看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调用栈中都调用,凡是遇见非数组元素,就将这个元素pushres中;遇到数组,也只是进入下一层调用中,将所有非数组元素推进去。这样就可以避免非数组元素被遍历两次了。

2018.11.27更新:
Underscore源码没有问题……今早睡醒突然在想flatten函数,发现我的遍历是不能满足strict参数的……源码应该是为了满足strict参数所以才遍历了两遍,没毛病。

你可能感兴趣的:(Underscore源码阅读:flatten)