JavaScript 递归

一个函数可以指向并调用自身。有三种方法可以达到这个目的:

  1. 函数名
  2. arguments.callee
  3. 作用域下的一个指向该函数的变量名

例如,思考一下如下的函数定义:

var foo = function bar() {
   // statements go here
};

在这个函数体内,以下的语句是等价的:

  1. bar()
  2. arguments.callee()
  3. foo()

调用自身的函数我们称之为递归函数。在某种意义上说,递归近似于循环。两者都重复执行相同的代码,并且两者都需要一个终止条件(避免无限循环或者无限递归)。例如以下的循环:

var x = 0;
while (x < 10) { // "x < 10" 是循环条件
   // do stuff
   x++;
}

可以被转化成一个递归函数和对其的调用:

function loop(x) {
  if (x >= 10) // "x >= 10" 是退出条件(等同于 "!(x < 10)")
    return;
  // 做些什么
  loop(x + 1); // 递归调用
}
loop(0);

不过,有些算法并不能简单的用迭代来实现。例如,获取树结构中所有的节点时,使用递归实现要容易得多:

function walkTree(node) {
  if (node == null) // 
    return;
  // do something with node
  for (var i = 0; i < node.childNodes.length; i++) {
    walkTree(node.childNodes[i]);
  }
}

loop函数相比,这里每个递归调用都产生了更多的递归。

将递归算法转换为非递归算法是可能的,不过逻辑上通常会更加复杂,而且需要使用堆栈。事实上,递归函数就使用了堆栈:函数堆栈。

这种类似堆栈的行为可以在下例中看到:

function foo(i) {
  if (i < 0)
    return;
  console.log('begin:' + i);
  foo(i - 1);
  console.log('end:' + i);
}
foo(3);

// 输出:

// begin:3
// begin:2
// begin:1
// begin:0
// end:0
// end:1
// end:2
// end:3

 

posted @ 2017-11-10 23:55  木头人_a  阅读(178)  评论(0编辑  收藏  举报