Javascript 这个递归函数是如何工作的?

Javascript 这个递归函数是如何工作的?,javascript,recursion,Javascript,Recursion,我看不出这个递归函数是如何工作的: function f(n) { g(n-1) } function g(n) { alert("before: " + n); if(n > 0) f(n); alert("after: " + n); } f(2)​;​ 我试图理解这段代码的工作原理,我看到了1之前、0之前和0之后是如何执行的,但是。。。1后是怎么来的 我认为它的执行是这样的。。。f2调用减去1的g,使“n”变为1。Alertbefore:+n执行前,1大于

我看不出这个递归函数是如何工作的:

function f(n) { g(n-1) }

function g(n) {
   alert("before: " + n);

   if(n > 0) f(n);

   alert("after: " + n);
}

f(2)​;​
我试图理解这段代码的工作原理,我看到了1之前、0之前和0之后是如何执行的,但是。。。1后是怎么来的

我认为它的执行是这样的。。。f2调用减去1的g,使“n”变为1。Alertbefore:+n执行前,1大于0,因此它将调用自身并减去1。Alertbefore:+n再次执行时,0不大于0,因此它将在:+n之后执行Alertbefore,函数结束

编辑:感谢@FlorianMargaine和@Cyrille帮助我理解这背后的逻辑=

请参见通话顺序:

Calls f() with parameter 2
f() calls g() with parameter 2-1 -> 1
g() alerts "before: 1"
since n > 0, g() calls f() with parameter 1 -- and blocks further execution of current g() with parameter 1 - in other words, it goes down in "funception"
f() calls g() with parameter 1-1 -> 0
g() alerts "before: 0"
since n === 0, it skips the if
g() alerts "after: 0"
finally, the blocked execution can resume because the inner function has finished executing, so it alerts "after: 1"
它不是真正的阻塞,它只是在执行当前函数的其余部分之前执行内部函数

要清楚地理解逻辑,只需试着按照逻辑说话。

参见通话顺序:

Calls f() with parameter 2
f() calls g() with parameter 2-1 -> 1
g() alerts "before: 1"
since n > 0, g() calls f() with parameter 1 -- and blocks further execution of current g() with parameter 1 - in other words, it goes down in "funception"
f() calls g() with parameter 1-1 -> 0
g() alerts "before: 0"
since n === 0, it skips the if
g() alerts "after: 0"
finally, the blocked execution can resume because the inner function has finished executing, so it alerts "after: 1"
它不是真正的阻塞,它只是在执行当前函数的其余部分之前执行内部函数

为了清楚地理解逻辑,只需试着通过说出它来遵循它。

在Javascript中,参数是通过值传递的,而不是通过引用传递的。因此在第一次迭代中,调用ifn>0fn;它调用gn-1并不减少n。当ifn>0 fn返回时,其值保持不变,并保持为1

这是一个调用图:

f(2) calls f(n) with n=2
├─ calls g(n-1), which is g(2-1) = g(1)
│   ├─ g(1) alerts "before: 1"
│   ├─ g(1), n > 0 ==> call f(1)
│   │   ├─ f(1) calls f(n) with n=1, but it's not the same "n"
│   │   │  as we're in another call (see the call tree?)
│   │   │   ├─ calls g(n-1), which is g(1-1) = g(0)
│   │   │   │   ├─ g(0) alerts "before: 0"
│   │   │   │   ├─ n == 0 ==> don't call f(n)
│   │   │   │   └─ g(0) alerts "after: 0"
│   │   └─ and that's all for f(1)
│   └─ g(1) alerts "after: 1"
└─ and that's all
1之后是因为在Javascript中,参数是通过值传递的,而不是通过引用传递的。因此在第一次迭代中,调用ifn>0fn;它调用gn-1并不减少n。当ifn>0 fn返回时,其值保持不变,并保持为1

这是一个调用图:

f(2) calls f(n) with n=2
├─ calls g(n-1), which is g(2-1) = g(1)
│   ├─ g(1) alerts "before: 1"
│   ├─ g(1), n > 0 ==> call f(1)
│   │   ├─ f(1) calls f(n) with n=1, but it's not the same "n"
│   │   │  as we're in another call (see the call tree?)
│   │   │   ├─ calls g(n-1), which is g(1-1) = g(0)
│   │   │   │   ├─ g(0) alerts "before: 0"
│   │   │   │   ├─ n == 0 ==> don't call f(n)
│   │   │   │   └─ g(0) alerts "after: 0"
│   │   └─ and that's all for f(1)
│   └─ g(1) alerts "after: 1"
└─ and that's all

现在更有意义了吗?N≤ 0是基本情况

function g(n) {
   alert("before: " + n);

   if(n > 0) g(n - 1);

   alert("after: " + n);
}

g(2)​;​

现在更有意义了吗?N≤ 0是基本情况

function g(n) {
   alert("before: " + n);

   if(n > 0) g(n - 1);

   alert("after: " + n);
}

g(2)​;​


每个函数调用最终返回。代码递归调用f/g,但在某个点n达到0,而f在if语句中未被调用,因此它在:0之后发出警报,并开始返回调用链,最终到达第一个堆栈帧,其中n为1。

每个函数调用最终返回。代码递归调用f/g,但在某个点n达到0,而f在if语句中没有被调用,因此它在:0之后发出警报,并开始返回调用链,最终到达第一个堆栈帧,其中n为1。

那么还有什么不清楚的呢?您似乎很好地遵循了逻辑。如果函数结束,如何调用after 1警报。那么有什么不清楚的呢?您似乎很好地遵循了逻辑。如果函数结束,如何调用after 1警报。除此之外,它最终在f1返回后调用after 1警报。我仍然不明白after 1警报是如何调用的:@Florian Margaine你说它不通过if是什么意思?我以为JS会跳过if语句,如果它的结果不是真的?@W3Geek是的,它会跳过它,只是用了一个错误的词:我的意思是条件没有通过,所以if被跳过了。哈哈,我想我现在明白多了。因此,通过funception,函数会在其下创建另一个函数,结果是0之前和0之后。之后,该函数会销毁它下面的内容,以便在1之后运行并发出警报?在不知道到底发生了什么的情况下看到这一点是非常令人困惑的。除此之外,f1返回后,它最终会在1后调用。我仍然不明白1后警报是如何调用的?:@Florian Margaine你说它不通过if是什么意思?我以为JS会跳过if语句,如果它的结果不是真的?@W3Geek是的,它会跳过它,只是用了一个错误的词:我的意思是条件没有通过,所以if被跳过了。哈哈,我想我现在明白多了。因此,通过funception,函数会在其下创建另一个函数,结果是0之前和0之后。之后,该函数会销毁它下面的内容,以便在1之后运行并发出警报?在不知道到底发生了什么的情况下看到这一点是非常令人困惑的。嗯,如果n不递减,函数似乎会运行一个无休止的循环。不,因为如果n>0 fn依次调用gn-1,那么它基本上不会无休止地循环。真的,这里的关键字是按值而不是按引用。你知道那是什么意思吗?不是因为你打电话给fn-1意味着n会改变。它将一直保留在这里,直到子程序返回。因此该函数基本上会在它下面创建一个新函数,然后在0之前和之后发出警报。在该函数完成执行上面的函数后,它会将其销毁。。。现在完成了自我执行。这就是after:1警报的来源。很好的解释。我会添加一些关于funcalls基本堆栈、调用帧的内容,因为OP在理解参数和局部变量如何工作方面似乎有问题。嗯,如果n不递减,函数似乎会运行一个无休止的循环。不,因为如果n>0,fn依次调用gn-1,那么基本上它不会无休止地循环。真的,这里的关键字是按值而不是按引用。你知道那是什么意思吗?不是因为你打电话给fn-1意味着n会改变
. 它将一直保留在这里,直到子程序返回。因此该函数基本上会在它下面创建一个新函数,然后在0之前和之后发出警报。在该函数完成执行上面的函数后,它会将其销毁。。。现在完成了自我执行。这就是after:1警报的来源。很好的解释。我想补充一点关于funcalls基本堆栈、调用帧的内容,因为OP在理解参数和局部变量如何工作方面似乎有问题。不,它仍然没有任何意义。我不知道如何调用1警报后=/当调用g0后的n=1时,将调用alertafter:+n。不,它仍然没有任何意义。我不知道如何调用1警报后=/当调用g0后的n=1时,将调用alertafter:+n。