递归调用javascript函数

Sam*_*ere 87 javascript recursion function function-expression

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

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

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

var copyFunction = functionHolder;
Run Code Online (Sandbox Code Playgroud)

copyFunction(3);输出3 2 1 0如上.如果我改变functionHolder如下:

functionHolder = function(whatever) {
    output("Stop counting!");
Run Code Online (Sandbox Code Playgroud)

然后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.

Arn*_*anc 143

使用命名函数表达式:

您可以为函数表达式提供一个实际上是私有的名称,并且只能从函数内部看到ifself:

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

这里myself只有内部功能的可视本身.

您可以使用此专用名称以递归方式调用该函数.

参见13. Function DefinitionECMAScript 5规范:

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

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

使用arguments.callee:

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

var factorial = function (n) {
    if (n <= 1) {
        return 1;
    }
    return n * arguments.callee(n-1);
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • +1虽然它在IE8中是一个小小的错误,但在"我自己"实际上在封闭变量环境中可见,并且它引用了实际"我自己"函数的*重复*.您应该能够将外部引用设置为"null". (4认同)

Fel*_*ing 10

您可以使用arguments.callee [MDN]访问该功能:

if (counter>0) {
    arguments.callee(counter-1);
}
Run Code Online (Sandbox Code Playgroud)

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

  • 我认为这已被弃用(严格模式下不允许) (6认同)

San*_*dro 5

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

  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);
Run Code Online (Sandbox Code Playgroud)


Nic*_*olò 5

您可以使用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)));
Run Code Online (Sandbox Code Playgroud)

您可以这样使用它:

// 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);
  }
});
Run Code Online (Sandbox Code Playgroud)