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); } });