自执行函数内递归函数的setTimeout()

6
我想将我的代码分发为自执行匿名函数,就像很多人这样做。同时,在我的代码中,我需要监控另一个库的加载,以便在其可用时使用该库。
(function(window, document, undefined) {
  staffHappens();
  var initMyLib = function() {
    if (typeof(myLib) == 'undefined') {
      setTimeout("initMyLib()", 50);
    } else {
      useMyLib();
    }
  }
  moreStaffHappens();
  initMyLib(); //-> initMyLib is undefined
})(this, document);

这个错误是如何发生的?initMyLib应该在包含它的(自执行)函数的范围内吗?
3个回答

12

setTimeout("initMyLib()", 50); 更改为 setTimeout(initMyLib, 50);

当您将字符串作为参数传递时,它将在超时触发时尝试进行求值,但它将在全局范围内运行。 而您的方法不存在于全局范围内。


演示请参见 http://jsfiddle.net/gaby/zVr7L/


@kTash,不是说它不应该出现,而是你确定undefined部分来自那一行而不是超时脚本的执行吗? - Gabriele Petrioli
我不知道。他说发生在那一行,所以我想他已经调试了很多来找出问题所在...这是我最先想到的事情,但如果它发生在指定的那一行而不是超时,那么我不确定这是否正确。我只是随口说出自己的想法。 - LoveAndCoding
@ktash,在我的测试中它运行得很好...所以我猜问题描述也有错误 :) - Gabriele Petrioli
@GabyakaG.Petrioli:嗨。如果我想将参数传递给函数怎么办?如果这样做,它会给我无限递归。 - Dharmraj
@Dharmraj,那么你需要使用匿名函数。setTimeout( function(){ initMyLib(params); }, 50); - Gabriele Petrioli
@GabyakaG.Petrioli:完美地工作了。感谢您快速帮助我 :) - Dharmraj

2

请参考此答案以获取一些线索:JavaScript中递归函数 vs setInterval vs setTimeout

这是该答案中的代码示例:

/*
this will obviously crash... and all recursion is at risk of running out of call stack and breaking your page...

function recursion(c){
    c = c || 0;
    console.log(c++);
    recursion(c);
}
recursion();

*/

// add a setTimeout to reset the call stack and it will run "forever" without breaking your page!
// use chrome's heap snapshot tool to prove it to yourself.  :)

function recursion(c){
    setTimeout(function(c){
        c = c || 0;
        console.log(c++);
        recursion(c);
    },0,c);
}

recursion();

// another approach is to use event handlers, but that ultimately uses more code and more resources

响应的第二部分是不正确的。超时并不会重置调用堆栈,尽管这个事实很少被人知道。我在Chrome和Safari上进行了测试 - 结果相同 - 无限堆栈增长。请参阅https://medium.com/@devinmpierce/recursive-settimeout-8eb953b02b98 - John Moore

2
你可以使用真正的匿名函数来避免作用域问题:
(function() {
    if(typeof(myLib) == 'undefined')
        setTimeout(arguments.callee, 50);
    else
        // loaded
})()

2
这是一个有效的解决方案,但由于arguments.callee在最近的JavaScript版本中已被弃用,因此您应该避免使用它,并使用建议的命名函数。这也可以通过(function name() {来完成,然后使用setTimeout(name, 50); - Gabriele Petrioli
@Gaby aka G. Petrioli:我知道。不过,“callee”是一个很棒的值得了解的特性。 - georg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接