递归引出问题
最近在看一本JS方面的书,名字叫《JavaScript语言精粹(修订版)》。在看到递归这一节的时候,有一段代码让我想了大概几分钟才想明白是怎么回事,代码如下:
var haoni = function(disc, src, aux, dst){ if(disc > 0){ haoni(disc - 1, src, dst, aux); document.writeln('Move disc ' + disc + ' from ' + src + ' to ' + dst + '<br/>'); haoni(disc -1, aux, src, dst); } }; haoni(3, 'Src', 'Aux', 'Dst');
这是一个有关“汉诺塔”的js实现代码。我主要是看过输出的结果有点不明白。输出如下:
Move disc 1 from Src to Dst Move disc 2 from Src to Aux Move disc 1 from Dst to Aux Move disc 3 from Src to Dst Move disc 1 from Aux to Src Move disc 2 from Aux to Dst Move disc 1 from Src to Dst
或许这样的输出还看不出什么。如果你在每句代码下添加一句打印的代码,结果就很让人迷糊了,如:
var haoni = function(disc, src, aux, dst){ document.write('111<br/>'); if(disc > 0){ document.write('222<br/>'); haoni(disc - 1, src, dst, aux); document.write('333<br/>'); document.writeln('Move disc ' + disc + ' from ' + src + ' to ' + dst + '<br/>'); document.write('444<br/>'); haoni(disc -1, aux, src, dst); document.write('555<br/>'); } document.write('666<br/> '); }; haoni(3, 'Src', 'Aux', 'Dst');
结果如下:
111 222 111 222 111 222 111 666 333 Move disc 1 from Src to Dst 444 111 666 555 666 333 Move disc 2 from Src to Aux 444 111 222 111 666 333 Move disc 1 from Dst to Aux 444 111 666 555 666 555 666 333 Move disc 3 from Src to Dst 444 111 222 111 222 111 666 333 Move disc 1 from Aux to Src 444 111 666 555 666 333 Move disc 2 from Aux to Dst 444 111 222 111 666 333 Move disc 1 from Src to Dst 444 111 666 555 666 555 666 555 666
如果你仔细看下打印的结果,你会发现结果很有意思,这里可以自己发现哦-_-!。
思考问题-查找资料
细心的人应该会看出上面的疑问。为什么会这样:
666 333 //为什么666之后会是333 Move disc 1 from Src to Dst
这里就要说点其他的了。
JavaScript中有一个重要的东西叫“执行环境”。执行环境定义了变量或者函数有权访问的其他数据,决定了他们各自的行为。而每个执行环境都有一个和他有关的变量对象,环境中定义的所有变量和函数都保存在这个对象中。每个函数都有自己的执行环境。当执行流进入一个函数的时候。函数的执行环境会被推入一个环境栈中。而函数执行之后栈会将他的执行环境弹出,把控制权返回给之前的执行环境。
这段话摘自:《JavaScript高级程序设计》。所以上面的问题就有了解答。
从一个例子解答问题
我们先来看一个小例子:
(function(num){ if(num > 1){ console.log('11'); arguments.callee(num-1); console.log('22'); } }(3));
结果:
11 11 22 22
根据上面资料的介绍,这个结果也就很好理解了。当第一次执行的时候num是3,进入if判断打印‘11’。然后调用自身,这个时候函数(这里叫A)会进入另外一个函数(这里叫B)而新的执行环境也会进来(虽然另外一个函数还是自身)。结果进入到新的函数之后再次if判断,结果符合条件就再次打印‘11’,然后执行环境再次被改变(这里叫C)。在新的执行环境内发现if不在满足,退出当前的执行环境(这里是从C退出),把控制权交给之前的执行环境(这里交给的是B不是A,然后打印‘22‘),而B在这时也不在满足条件就在交给最初的A,然后A继续接下来的执行打印’22‘。
因此
这样上面的结果也就比较好理解了,只是函数里面有两个递归,当第一个递归把控制权交回到最开始的时候是第二个递归的开始,所以就会看到打印的结果跳来跳去,所以使用递归的时候一定要注意。