重写展平函数

Rewriting the flatten function

我写了下划线 _.flatten 函数的一个版本,它可以工作,但我不明白为什么第一个版本有效而​​第二个版本不行。

var flatten = function (array, result) {
  var result = [];
  for (var i = 0; i < array.length; i++) {
    var current = array[i];
    if (Array.isArray(current)) {
      result.push.apply(array, flatten(current));
    }
    else {
      result.push(current);
    }
  }
  return result;
};

var nested = [1, [2], [3, [[[4]]]]];
console.log(flatten(nested));
// [1,2,3,4]

对比:

var flatten = function (array, result) {
  var result = [];
  array.forEach(function (current) {
    if (Array.isArray(current)) {
      result.push.apply(array, flatten(current));
    }
    else {
      result.push(current);
    }
  });
  return result;
};
var nested = [1, [2], [3, [[[4]]]]];
console.log(flatten(nested));
// [1]

您将错误的第一个参数传递给 result.push.apply:

result.push.apply(array, flatten(current));
// ---------------^^^^^

您想在 result 上呼叫 push,但您实际上是在呼叫 array.push。你想说:

result.push.apply(result, flatten(current));

以便 push 在被调用时得到 result 作为它的 this


一个更有趣的问题是,为什么您基于 for 的实现 "works" 尽管其实现很混乱。如果你抛出一个

console.log(nested);

在第一个的底部,您会看到基于 for 的实现弄乱了它的论点;因此 "works".

上的惊吓引用

如果您在心理上逐步完成 if

if (Array.isArray(current)) {
  result.push.apply(array, flatten(current));
}
else {
  result.push(current);
}

您会看到它在迭代时正在修改 array。但是它添加到 array 的所有内容都是另一个数组,而非数组最终会出现在 result 您想要的位置。

如果您开始于:

var nested = [1, [2], [3, [[[4]]]], 5]

你会发现它不能以另一种方式工作,因为你最终会得到:

[1, 5, 2, 3, 4]

作为你的最终结果(以及 nested 中的一些其他混乱)。您的基于 for 的实现之所以有效,是因为您碰巧给了它一个特殊结构的输入。