Javascript 递归调用javascript函数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7065120/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 00:41:09  来源:igfitidea点击:

Calling a javascript function recursively

javascriptfunctionrecursionfunction-expression

提问by Samthere

I can create a recursive function in a variable like so:

我可以在一个变量中创建一个递归函数,如下所示:

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

With this, functionHolder(3);would output 3210. Let's say I did the following:

有了这个,functionHolder(3);将输出3210. 假设我做了以下事情:

var copyFunction = functionHolder;

copyFunction(3);would output 3210as above. If I then changed functionHolderas follows:

copyFunction(3);将输出3210如上。如果我然后更改functionHolder如下:

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

Then functionHolder(3);would give Stop counting!, as expected.

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

copyFunction(3);now gives 3Stop counting!as it refers to functionHolder, not the function (which it itself points to). This could be desirable in some circumstances, but is there a way to write the function so that it calls itself rather than the variable that holds it?

copyFunction(3);现在给出3Stop counting!它所指的functionHolder,而不是函数(它本身指向的)。在某些情况下这可能是可取的,但是有没有办法编写函数以便它调用自身而不是保存它的变量?

That is, is it possible to change onlythe line functionHolder(counter-1);so that going through all these steps still gives 3210when we call copyFunction(3);? I tried this(counter-1);but that gives me the error this is not a function.

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

回答by Arnaud Le Blanc

Using Named Function Expressions:

使用命名函数表达式:

You can give a function expression a name that is actually privateand is only visible from inside of the function ifself:

您可以为函数表达式指定一个名称,该名称实际上是私有的,并且仅在 ifself 的函数内部可见:

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

Here myselfis visible only inside of the functionitself.

下面myself该函数的可见里面只有自己。

You can use this private name to call the function recursively.

您可以使用此私有名称递归调用该函数。

See 13. Function Definitionof the ECMAScript 5 spec:

请参阅13. Function DefinitionECMAScript 5 规范:

The Identifier in a FunctionExpression can be referenced from inside the FunctionExpression's FunctionBody to allow the function to call itself recursively. However, unlike in a FunctionDeclaration, the Identifier in a FunctionExpression cannot be referenced from and does not affect the scope enclosing the FunctionExpression.

FunctionExpression 中的标识符可以从 FunctionExpression 的 FunctionBody 内部引用,以允许函数递归调用自身。但是,与 FunctionDeclaration 不同的是,FunctionExpression 中的 Identifier 不能被引用并且不会影响包含 FunctionExpression 的范围。

Please note that Internet Explorer up to version 8 doesn't behave correctly as the name is actually visible in the enclosing variable environment, and it references a duplicate of the actual function (see patrick dw's comment below).

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

Using arguments.callee:

使用arguments.callee:

Alternatively you could use arguments.calleeto refer to the current function:

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

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

The 5th edition of ECMAScript forbids use of arguments.callee() in strict mode, however:

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

(From MDN): In normal code arguments.callee refers to the enclosing function. This use case is weak: simply name the enclosing function! Moreover, arguments.callee substantially hinders optimizations like inlining functions, because it must be made possible to provide a reference to the un-inlined function if arguments.callee is accessed. arguments.callee for strict mode functions is a non-deletable property which throws when set or retrieved.

(来自MDN):在普通代码中,arguments.callee 指的是封闭函数。这个用例很弱:只需命名封闭函数!此外,arguments.callee 大大阻碍了像内联函数这样的优化,因为如果访问 arguments.callee,必须能够提供对未内联函数的引用。严格模式函数的 arguments.callee 是一个不可删除的属性,它在设置或检索时抛出。

回答by Felix Kling

You can access the function itself using arguments.callee[MDN]:

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

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

This will break in strict mode, however.

但是,这将在严格模式下中断。

回答by Nicolò

You can use the Y-combinator: (Wikipedia)

您可以使用 Y 组合器:(维基百科

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

And you can use it as this:

你可以这样使用它:

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

回答by Sandro

I know this is an old question, but I thought I'd present one more solution that could be used if you'd like to avoid using named function expressions. (Not saying you should or should not avoid them, just presenting another solution)

我知道这是一个老问题,但我想我会提出另一个解决方案,如果您想避免使用命名函数表达式,则可以使用该解决方案。(不是说你应该或不应该避免它们,只是提出另一种解决方案)

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

回答by Cody

Here's one very simple example:

这是一个非常简单的例子:

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

Notice that the countercounts "backwards" in regards to what slug's value is. This is because of the position at which we are logging these values, as the function recursbefore logging -- so, we essentially keep nesting deeper and deeper into the call-stackbeforelogging takes place.

请注意,与' ' 的值counter有关的计数“向后” slug。这是因为在其中我们会记录这些值的位置,作为函数复发日志记录之前-所以,我们基本上保持筑巢越陷越深到调用堆栈之前记录发生。

Once the recursion meets the final call-stack item, it trampolines"out" of the function calls, whereas, the first increment of counteroccurs inside of the last nested call.

一旦递归满足最终调用堆栈的项目,它蹦床函数调用的“走出去”,而中,第一个增量counter内的最后一个嵌套调用的发生。

I know this is not a "fix" on the Questioner's code, but given the title I thought I'd generically exemplify Recursionfor a better understanding of recursion, outright.

我知道这不是对发问者代码的“修复”,但鉴于标题,我想我会一般地举例说明递归,以便更好地理解递归,彻底的。