Javascript 这个递归函数的操作顺序是什么?

Javascript 这个递归函数的操作顺序是什么?,javascript,recursion,Javascript,Recursion,javascript中的递归是我无法理解的。我快到了,但我对操作顺序有点困惑。以以下函数为例: function rangeOfNumbers(startNum, endNum) { if (endNum - startNum === 0) { return [startNum]; } else { var numbers = rangeOfNumbers(startNum, endNum - 1); numbers.push(endNum); retur

javascript中的递归是我无法理解的。我快到了,但我对操作顺序有点困惑。以以下函数为例:

function rangeOfNumbers(startNum, endNum) {
  if (endNum - startNum === 0) {
    return [startNum];
  } else {
    var numbers = rangeOfNumbers(startNum, endNum - 1);
    numbers.push(endNum);
    return numbers;
  }
}
我想弄清楚的问题是:

  • 这是否会在
    数字
    周围创建一个闭包

  • 函数
    rangeOfNumbers
    是否每次返回
    numbers
    ,还是仅在递归完成后返回

  • 对于基本情况:为什么我们返回一个只有
    startNum
    作为“基本条件”的数组?这不会在返回时覆盖
    numbers
    变量吗?我不确定基本情况到底是如何运作的。它似乎总是返回一些我们不想要的东西,但它控制函数何时停止执行(在函数调用的第一次开始时和/或结束时)


  • 考虑一下对功能的这种更改,唯一的区别是日志记录:

    function rangeOfNumbers(startNum, endNum) {
      if (endNum - startNum === 0) {
        console.log("Reached base case, returning: ");
        console.log([startNum]);
        return [startNum];
      } else {
        var numbers = rangeOfNumbers(startNum, endNum - 1);
        numbers.push(endNum);
        console.log("Returning " + numbers.join(','));
        return numbers;
      }
    }
    
    然后我们这样称呼它:

    rangeOfNumbers(0, 7);
    
    它会记录下来

    Reached base case, returning: 
    VM60:4 [0]
    VM60:9 Returning 0,1
    VM60:9 Returning 0,1,2
    VM60:9 Returning 0,1,2,3
    VM60:9 Returning 0,1,2,3,4
    VM60:9 Returning 0,1,2,3,4,5
    VM60:9 Returning 0,1,2,3,4,5,6
    VM60:9 Returning 0,1,2,3,4,5,6,7
    
    所以没有结束。在递归中,您将一直挖掘到基本情况,然后将其添加到数组中,一直备份

    这是否会在数字周围创建一个闭包

    否,
    numbers
    变量名只有在单个块(好吧,函数)中才是可引用的,并且它结束时,其他变量都没有引用它。没有保存对它的引用,因此它不是一个闭包

    函数rangeOfNumbers是每次返回数字,还是仅在递归完全完成后返回数字

    在每个递归情况下,都会返回
    numbers
    变量。在基本情况下,将返回一个新数组。但由于在递归情况下,
    numbers
    变量是对递归调用返回的数组的引用,因此每次在链上返回的数组都是相同的数组

    对于基本情况:为什么我们返回一个只有startNum作为“基本条件”的数组?这不会在返回时覆盖数字变量吗

    在基本情况下,没有<代码>编号变量(除非考虑从未引用的吊挂变量名)。返回的数组在递归调用中成为
    numbers
    变量,但在基本情况下,它不是
    numbers
    变量

    请记住,函数的每次调用都会导致其变量的单独绑定。例如,与

    function fn(count) {
      const num = Math.random();
      return num + (count > 1 ? fn(count - 1) : 0);
    }
    
    fn
    的每个调用都有一个单独的
    num
    变量,它是一个随机数。递归调用不会覆盖以前调用中的任何内容

    对于您的
    RangeofNumber
    ,最后的递归调用将产生基本情况:
    [startNum]
    数组。这将返回给递归调用方,并存储到中该特定调用方的
    numbers
    变量中


    然后,一个调用者向数组中添加一个项并将其返回给它的调用者,该过程将重复,直到递归调用堆栈完全展开。

    这里有一种方法可以通过增加
    start
    来编写
    range
    函数-

    const range=(开始、结束)=>
    开始>结束//终止条件
    ? [//基本情况
    :[start,…range(start+1,end)]//递归步骤
    控制台日志(范围(5,10))
    
    //[5,6,7,8,9,10]
    没有结束。在递归中,您将一直挖掘到基本情况,然后将其添加到数组中,一直备份。如果您只是在函数中的一些关键点添加了一些对
    console.log
    的调用,这对您来说是非常容易理解的。@JaredSmith我也这么认为,但我认为这比添加console.log要复杂一些,因为事情发生的顺序不太清楚。也许我走得不够远?如果你不同意,并相信这将是简单的,请让我知道如何做!如果我们在最初调用变量时将函数调用赋给它,例如,让myRecursiveArray=rangeOfNumbers(0,7);-->那么基本情况会覆盖它吗?不会,整个
    rangeOfNumbers
    调用堆栈将在分配变量之前解析,并且其结果是否分配给变量对递归函数的内部没有影响。在这段代码中,任何地方都不会被覆盖。为什么它在返回之前先到达基本情况?我本以为一切都会反过来发生。。。因为每次调用函数时,函数都会将endNum推到数组的末尾。@gogogadgetinternet,因为它是递归的:它会在返回之前调用自己。所以它用0和7调用,然后用0和6再次调用自己(在返回之前),然后调用用0和5调用自己,以此类推。在你到达基本情况之前,不会有任何实际返回,然后它会一路返回调用堆栈,将每个连续返回添加到数组中。我现在明白了!!!非常感谢你。我不知道为什么我很难看到发生了什么。我觉得我有了更好的理解。它一直深入到基本情况,然后开始按
    numbers.push(endNum)
    ,每次它都会在调用堆栈中反向执行,直到没有任何事情可做为止。我在想,它会一直持续下去,直到它达到基本情况,在那之前一切都发生了。非常感谢您的解释。@gogogadgetinternet没问题。不要难过,在您实际跟踪执行的每个步骤之前,要理解它并不容易(日志记录对此很有帮助)。如果这个答案对你有效,请接受。还要注意的是:因为它在返回任何内容之前会一直深入到基本情况,所以如果太深,可能会溢出堆栈:调用
    rangeOfNumbers(0,x)
    var numbers = rangeOfNumbers(startNum, endNum - 1);