我可以在变量中创建一个递归函数,如下所示:
/* 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
,而不是函数(它本身指向的)。在某些情况下这可能是理想的,但是有没有办法编写函数以便它调用自身而不是保存它的变量?
也就是说,是否可以更改 行functionHolder(counter-1);
,以便完成所有这些步骤仍然会3
2
1
我们致电0
时copyFunction(3);
?我尝试了this(counter-1);
,但这给了我错误this is not a function
。
答案 0 :(得分:139)
您可以为函数表达式指定一个实际上是私有的名称,并且只能从函数内部看到ifself:
var factorial = function myself (n) {
if (n <= 1) {
return 1;
}
return n * myself(n-1);
}
typeof myself === 'undefined'
此处myself
仅在函数本身内部可见。
您可以使用此私有名称递归调用该函数。
请参阅ECMAScript 5规范的13. Function Definition
:
可以从FunctionExpression的FunctionBody内部引用FunctionExpression中的标识符,以允许函数递归调用自身。但是,与FunctionDeclaration不同,FunctionExpression中的标识符不能被引用,也不会影响包含FunctionExpression的范围。
请注意,版本8以下的Internet Explorer行为不正确,因为名称实际上在封闭变量环境中可见,并且它引用了实际函数的副本(请参阅 patrick dw '的评论如下)。
或者,您可以使用arguments.callee
来引用当前函数:
var factorial = function (n) {
if (n <= 1) {
return 1;
}
return n * arguments.callee(n-1);
}
第5版ECMAScript禁止在strict mode中使用arguments.callee():
(From MDN):在普通代码中,arguments.callee引用封闭函数。这个用例很弱:只需命名封闭函数!而且,arguments.callee实际上阻碍了内联函数之类的优化,因为如果访问arguments.callee,必须能够提供对非内联函数的引用。用于严格模式函数的arguments.callee是一个不可删除的属性,在设置或检索时抛出。
答案 1 :(得分:8)
答案 2 :(得分:5)
我知道这是一个老问题,但我想如果您想避免使用命名函数表达式,我还会提供一个可以使用的解决方案。 (不是说你应该或不应该避免它们,只是提出另一种解决方案)
<?php
/**
* The entity class.
*/
class Test
{
public $id;
public $info;
}
class Test_model extends Base_repository
{
/**
* Tell what table we are using.
*/
public function __construct()
{
parent::__construct();
$this->table = 'test';
}
/**
* "Overload" save method and call it from the parent.
*
* @param test $item Make use of the Dependency Injection.
* @return void
*/
public function save(Test $item)
{
parent::save($item);
}
}
答案 3 :(得分:4)
您可以使用Y-combinator:(Wikipedia)
// 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);
}
});
答案 4 :(得分: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
的值是“向后”。这是因为我们记录这些值的位置,因为函数在记录之前重复 - 所以,我们基本上更深入地嵌套到调用堆栈 在日志记录发生之前。
一旦递归符合最终的调用堆栈项,它就会 trampolines “out”函数调用,而counter
的第一个增量发生在最后一个嵌套调用中。
我知道这不是查询者代码的“修复”,但鉴于标题,我认为我通常会举例说明递归,以便更好地理解递归,直接。