recursion调用一个javascript函数

我可以像这样在一个variables中创build一个recursion函数:

/* Count down to 0 recursively. */ var functionHolder = function (counter) { output(counter); if (counter > 0) { functionHolder(counter-1); } } 

有了这个, functionHolder(3); 会输出3 2 1 0 。 假设我做了以下事情:

 var copyFunction = functionHolder; 

copyFunction(3); 将如上输出3 2 1 0 。 如果我然后改变functionHolder如下:

 functionHolder = function(whatever) { output("Stop counting!"); 

然后functionHolder(3); 会给Stop counting! ,如预期。

copyFunction(3); 现在给3 Stop counting! 因为它引用了functionHolder ,而不是函数(它自己指向的)。 这在某些情况下是可取的,但是有没有办法编写这个函数,让它自己调用而不是variables呢?

也就是说,是否可以改变行functionHolder(counter-1); 所以当我们调用copyFunction(3);时,经过所有这些步骤仍然给出3 2 1 0 copyFunction(3); ? 我试过this(counter-1); 但是这给了我错误, this is not a function

使用命名函数expression式:

你可以给一个函数expression式一个名字,这个名字实际上是私有的 ,只能从函数内部看见ifself:

 var factorial = function myself (n) { if (n <= 1) { return 1; } return n * myself(n-1); } typeof myself === 'undefined' 

这里myself 只在函数本身内部可见

您可以使用这个专用名称recursion调用函数。

请参阅13. Function Definition ECMAScript 5规范的13. Function Definition

FunctionExpression中的标识符可以从FunctionExpression的FunctionBody中引用,以允许函数以recursion方式调用它自己。 但是,与FunctionDeclaration不同,FunctionExpression中的标识符不能被引用,也不会影响包含FunctionExpression的范围。

请注意,Internet Explorer版本8的行为不正确,因为名称在封闭variables环境中实际上是可见的,并且引用了实际函数的副本(请参见下面的patrick dw的注释)。

使用arguments.callee:

或者,您可以使用arguments.callee引用当前函数:

 var factorial = function (n) { if (n <= 1) { return 1; } return n * arguments.callee(n-1); } 

ECMAScript的第5版禁止在严格模式下使用arguments.callee(),但是:

(来自MDN ):在正常的代码arguments.callee是指封闭函数。 这个用例很弱:简单地命名封闭的函数! 此外,arguments.callee会严重阻碍内联函数之类的优化,因为如果访问arguments.callee,必须提供对非内联函数的引用。 用于严格模式函数的arguments.callee是一个不可删除的属性,在设置或检索时抛出。

您可以使用arguments.callee [MDN]访问函数本身:

 if (counter>0) { arguments.callee(counter-1); } 

然而,这将在严格的模式中打破。

我知道这是一个古老的问题,但我想我会提出一个更多的解决scheme,可以使用,如果你想避免使用命名函数expression式。 (不是说你应该或不应该避免他们,只是提出另一种解决scheme)

  var fn = (function() { var innerFn = function(counter) { console.log(counter); if(counter > 0) { innerFn(counter-1); } }; return innerFn; })(); console.log("running fn"); fn(3); var copyFn = fn; console.log("running copyFn"); copyFn(3); fn = function() { console.log("done"); }; console.log("fn after reassignment"); fn(3); console.log("copyFn after reassignment of fn"); copyFn(3); 

这里有一个非常简单的例子:

 var counter = 0; function getSlug(tokens) { var slug = ''; if (!!tokens.length) { slug = tokens.shift(); slug = slug.toLowerCase(); slug += getSlug(tokens); counter += 1; console.log('THE SLUG ELEMENT IS: %s, counter is: %s', slug, counter); } return slug; } var mySlug = getSlug(['This', 'Is', 'My', 'Slug']); console.log('THE SLUG IS: %s', mySlug); 

请注意, counter计数“倒退”的是什么slug的价值。 这是因为我们logging这些值的位置,因为该函数在logging之前重复出现 – 所以,在logging发生之前,我们基本上将嵌套更深入地embedded到调用堆栈中

一旦recursion满足最终的调用栈项目,它将蹦出函数调用的“out”,而counter的第一个增量发生在最后的嵌套调用中。

我知道这不是对问题代码的“修正”,但是考虑到标题,我想我一般会举例说明recursion ,以更好地理解recursion,直接。

你可以使用Y-combinator 🙁 维基百科 )

 // ES5 syntax var Y = function Y(a) { return (function (a) { return a(a); })(function (b) { return a(function (a) { return b(b)(a); }); }); }; // ES6 syntax const Y = a=>(a=>a(a))(b=>a(a=>b(b)(a))); // If the function accepts more than one parameter: const Y = a=>(a=>a(a))(b=>a((...a)=>b(b)(...a))); 

你可以这样使用它:

 // ES5 var fn = Y(function(fn) { return function(counter) { console.log(counter); if (counter > 0) { fn(counter - 1); } } }); // ES6 const fn = Y(fn => counter => { console.log(counter); if (counter > 0) { fn(counter - 1); } });