函数的递归调用

  递归,就是函数内部自己调用自己,首先要把它想成函数的调用,如果一个函数递归调用了(自己调用了自己)5次,可以把它想成5个函数的调用,fn1 调用fn2(), fn2调用fn3(), fn3调用了fn4(), fn4调用了fn5(), 只不过fn1, fn2, fn3, fn4, fn5 都是一样的函数。函数的每一次调用,都会栈中开辟一块空间,保存着这个函数调用的所有信息,比如传递过来的参数,如果该函数没有执行完,这些信息是不会释放的。fn1调用fn2() , 如果fn2()没有完成,并返回到fn1,fn1的空间永远都不会释放,它里面的保存的信息会一直存在。同理,fn2 调用了fn3(), 如果fn3没有执行完,fn2的空间也就不会释放。 正是由于函数不停地调用,不停地分配内存空间,总有一个函数要返回,不会再调用函数,否则内存就使用完了。对于递归来说,就是函数不会再调用自己了,这也就是说,递归函数的内部肯定存在一个条件,当达到这个条件的时候,函数不会调用函数(自己)。函数的返回是返回到调用它的函数的地方,fn5执行完了,永远都是返回到fn4, fn4在执行的时候,是从调用fn5() 的地方开始向下继续执行,  相当于在 fn4中,调用函数结束了,开始执行调用函数语句下面的语句。当一个函数调用另一个函数的时候,被调用的函数永远都是从函数第一行语句开始执行。

 

  简单写一个打印函数理解一下

function upAndDown(n) {
    console.log("before call " + n);
    if (n < 3) {
        upAndDown(n + 1);
    }
    console.log("after call " + n);
}

upAndDown(1)

  程序执行时,调用upAndDown(1), 函数接受到参数1,开始执行,把这次调用看做是fn2, 输出了 "before call 1",  if 判断(1< 3)成立,函数调用自己,看做一次普通的函数调用就可以了,就叫fn3吧,fn2 调用了fn3,把参数2传递过来,函数fn3开始执行,函数执行永远都是从函数第一行语句开始执行,它输出了"before call 2"。 这里要注意的是函数fn2并没有执行完, 它还等着fn3的返回,并执行后面的console.log语句,所以n =1 是会保存起来的。函数fn3() 继续执行,还是if 判断(2 < 3) 成立,继续调用自己,生成了一次函数的调用fn4,并把参数3,传递过去,此时,fn3 也没有执行完成,它在等待fn4 的执行结果。f4 接受到参数3,开始执行,输出"before call 3",进行if 判断,由于3 < 3 不成立,if代码块不会执行,函数终于不用再继续调用自己了,同时,函数也有机会执行到if 后面的语句了,输出"after call 3", 由于console.log() 后面没有语句了,函数执行完毕,fn4调用完了,它会返回到调用它的fn3 函数,fn3 终于有机会执行了,它会继续执行函数调用语句后面的语句,也就是console.log("after call " + n), 由于fn3 的空间中保存着n的信息,那就是2, 所以输出了"after call 2"。 fn3 执行完,返回到fn2, fn2 同样是输出 "after call 1" ,  然后返回,主程序中upAndDown(1) 后面并没有执行语句,所以整个程序结束。再画一张图

  函数纵然是自己调用自己,但每一次的调用都是一个全新的函数, 栈中为这个函数调用开辟的空间中保存着这个函数调用的完整信息,这个函数没有执行完之前,这些信息永远都会存在,也就是函数返回时,它依然能够获取调用时的信息。  

 

posted @ 2021-04-17 15:00  SamWeb  阅读(87)  评论(0编辑  收藏  举报