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參數所以才遍歷了兩遍,沒毛病。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容