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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容