递归如何在倒计时函数中工作

How does recursion work in a Countdown function

我正在学习一些 JavaScript,但我很难理解 FreeCodeCamp 上关于递归倒计时的课程 (link)。

在课程中,有这个初始示例。但我对它的运作方式感到困惑:

function countup(n) {
  if (n < 1) {
    return [];
  } else {
    const countArray = countup(n - 1);
    countArray.push(n);
    return countArray;
  }
}
console.log(countup(5));

我看到此函数执行的步骤(按顺序),如下所示:

我知道这不是它的工作原理,我只是在“描述”我的菜鸟思维如何看待这个递归函数。

我看了习题的解法和别人的解释,但是我无法理解为什么函数没有像我描述的那样工作,以及为什么在达到n==0之后,它开始填充一个数组从 1 数到 5。

我想了解一下。

这里是每个函数调用中数组的样子,如果有帮助的话:

Array: 1             N: 1 
Array: 1,2           N: 2
Array: 1,2,3         N: 3
Array: 1,2,3,4       N: 4
Array: 1,2,3,4,5     N: 5

如果你把控制台日志像这样放在这里,你可以更好地形象化它。

function countup(n) {
  if (n < 1) {
    return [];
  } else {
    const countArray = countup(n - 1);
    countArray.push(n);
    console.log(`countup(${n}) returns ${countArray}`);
    return countArray;
  }
}
console.log(countup(5));

发生的事情是,一旦您点击了 countup 的第一个调用,执行就会移动到那里。所以,用 5 调用它,你会看到第一个输出语句是 运行 和 1,因为多次它基本上调用了自己。

countup (5) 调用 countup (4) 调用 countup(3) 调用 countup (2) 调用 countup (1) -- countup (1) returns 1,返回到 2 的调用者 which returns 2, 1,返回到 3 的调用者 returns 3, 2, 1...等等最多 5 个。

您可以只添加一些日志记录来可视化正在发生的事情:

function countup(n) {
  if (n < 1) {
    console.log('n = %d, returning empty array', n);
    return [];
  } else {
    console.log('n = %d, calling countup(%d - 1)', n, n);
    const countArray = countup(n - 1);
    console.log('n = %d, countArray is %s', n, JSON.stringify(countArray))
    console.log('n = %d, pushing n onto array', n);
    countArray.push(n);
    console.log('n = %d, returning %s', n, JSON.stringify(countArray));
    return countArray;
  }
}
console.log(countup(5));

意识到 countup 的每次执行都会有自己的 ncountArray 变量。

想象一下可能会有所帮助。每个执行上下文被可视化为一个“盒子”。当函数调用 returns.

时,外框中的变量仍然存在

最外面的框是初始调用创建的执行上下文:countup(5):

// n is 5 and does not change
const countArray = countup(n - 1);
+-------------------------------------------------------------------------------+
|  // n is 4 and does not change                                                |
|  const countArray = countup(n - 1);                                           |
|  +-------------------------------------------------------------------------+  |
|  |  // n is 3 and does not change                                          |  |
|  |  const countArray = countup(n - 1);                                     |  |
|  |  +-------------------------------------------------------------------+  |  |
|  |  |  // n is 2 and does not change                                    |  |  |
|  |  |  const countArray = countup(n - 1);                               |  |  |
|  |  |  +-------------------------------------------------------------+  |  |  |
|  |  |  |  // n is 1 and does not change                              |  |  |  |
|  |  |  |  const countArray = countup(n - 1);                         |  |  |  |
|  |  |  |  +-------------------------------------------------------+  |  |  |  |
|  |  |  |  |  // n is 0 and does not change                        |  |  |  |  |
|  |  |  |  |  return []; // the if-block is executed because n < 1 |  |  |  |  |
|  |  |  |  +-------------------------------------------------------+  |  |  |  |
|  |  |  |  // countArray is []                                        |  |  |  |
|  |  |  |  countArray.push(n); // n is still 1                        |  |  |  |
|  |  |  |  return countArray; // returns [1]                          |  |  |  |
|  |  |  +-------------------------------------------------------------+  |  |  |
|  |  |  // countArray is [1]                                             |  |  |
|  |  |  countArray.push(n); // n is still 2                              |  |  |
|  |  |  return countArray; // returns [1, 2]                             |  |  |
|  |  +-------------------------------------------------------------------+  |  |
|  |  // countArray is [1, 2]                                                |  |
|  |  countArray.push(n); // n is still 3                                    |  |
|  |  return countArray; // returns [1, 2, 3]                                |  |
|  +-------------------------------------------------------------------------+  |
|  // countArray is [1, 2, 3]                                                   |
|  countArray.push(n); // n is still 4                                          |
|  return countArray; // returns [1, 2, 3, 4]                                   |
+-------------------------------------------------------------------------------+
// countArray is [1, 2, 3, 4]
countArray.push(n); // n is still 5
return countArray; // returns [1, 2, 3, 4, 5]