Javascript 这个递归函数的操作顺序是什么?
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
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);