递归函数就是会直接或者间接调用自身的一种函数。递归是一种强大的编程技术,它把一个问题分解为一组相似的子问题,调用自身去解决它的子问题。 一、汉诺塔

问题描述:有3根柱子和一套直径各不相同的空心圆盘。开始时源柱子上的所有圆盘都按照从小到大的顺序堆叠。目标是通过每次移动一个圆盘到另一根柱子,最终把一堆圆盘移动到目标柱子上,过程中不允许把较大的圆盘放置在较小的圆盘上。 var hanoi=function(disc,src,aux,dst){ if(disc>0){ hanoi(disc-1,src,dst,aux); console.log("move "+disc+" from "+src+" to "+dst); hanoi(disc-1,aux,src,dst); } } 圆盘数量为3的时候解法为:

hanoi函数把一堆圆盘从一根柱子移动到另一根柱子,必要时使用辅助的柱子。 它把问题分解成3个子问题: 首先,移动一对圆盘中较小的圆盘到辅助柱子上,从而露出下面较大的圆盘。 然后,移动下面较大的圆盘到目标柱子上。 最后,它将刚才较小的圆盘从辅助的柱子上再移动到目标柱子上。 传递给hanoi函数的参数包括当前移动的圆盘编号和它将用到的3根柱子。当它调用自身的时候,它去处理当前正在处理的圆盘之上的圆盘。最终,它会以一个不存在的圆盘编号去调用。此时,不执行任何操作。由于该函数对非法值不予理会,就不用担心它会导致死循环。 二、DOM遍历

递归函数可以非常高效地操作树形结构,比如浏览器文档对象模型DOM。每次递归调用处理指定树的一小段。 html结构如下: <body><div class="test">测试div</div><span class="test">测试span</span><div class="test1">test1 div</div></body> js如下: <script>/定义walk_the_DOM函数,它从某个指定的节点开始,按HTML源码中的顺序访问该树的每个节点。 它会调用一个函数,并依次传递每个节点给它。walk_the_DOM调用自身去处理每一个子节点。/var walk_the_DOM=function walk(node,func){ func(node); node=node.firstChild; while(node){ walk(node,func); node=node.nextSibling; } } /定义getElementsByAttribute函数。它以一个属性名称字符串和一个可选的匹配值作为参数。 它调用walk_the_DOM,传递一个用来查找节点属性名的函数作为参数。 匹配的节点会累加到一个结果数组中。/var getElementsByAttribute=function(att,value){ var results=[]; walk_the_DOM(document.body,function(node){ var actual=node.nodeType===1&&node.getAttribute(att); if(typeof actual==='string' &&( actual===value|| typeof value!=='string')){ results.push(node); } }); return results; } console.log(getElementsByAttribute("class","test"));//[div.test, span.test] 三、命名函数表达式和递归

1、递归问题 求阶乘的函数: function factorial(num){ if(num<=1){ return 1; }else{ return num*factorial(num-1); } } 正常情况运行没问题,但是下面操作会让它出错: var anotherFactorial=factorial;//把函数保存在遍历anotherFactorial中 factorial=null;//factorial置为null,此时指向原始函数的引用只剩一个 anotherFactorial(3)

factorial已经不再是函数,所以会报错。 2、arguments.callee实现递归 arguments.callee是一个指向正在执行的函数的指针,因此可以用它来实现对函数的递归调用 //consolefunction factorial(num){ if(num<=1){ return 1; }else{ return num*arguments.callee(num-1); } }var anotherFactorial=factorial; factorial=null; anotherFactorial(3) //6 用arguments.callee代替函数名,可以确保无论怎样调用函数都不会出问题。因此,在编写递归函数时,使用arguments.callee总比使用函数名更保险。 问题:在严格模式下,不能通过脚本访问arguments.callee,访问这个属性会报错。

更多严格模式相关内容可参考:javascript 语句和严格模式(三) 3、命名函数表达式实现递归 创建一个名为f()的命名函数表达式,然后赋值给factorial,即使把函数赋值给了另一个变量,函数的名字f仍然有效,所以递归调用照样能正常完成。 这种方式在严格模式和非严格模式都可行。 //consolevar factorial =function f(num){'use strict' if(num<=1){ return 1; }else{ return num*f(num-1); } } factorial(3)//6var anotherFactorial=factorial; factorial=null; anotherFactorial(3)//6