为什么这个setTimeout()调用在控制台中工作但不是作为greasemonkey脚本?

时间:2011-09-09 03:49:04

标签: javascript greasemonkey settimeout

当我在greasemonkey脚本中的for()循环中使用setTimeout()时,它似乎根本不起作用。但是,如果我在Firebug控制台中运行它,完全相同的代码工作正常。这是代码:

// ==UserScript==
// @name           setTimeout test
// @include        *
// @run-at         document-end
// ==/UserScript=


function test(delaytime) {
    alert("test called with "+delaytime);
}

function test2() {
  for( var i = 0; i < 100; i+= 10 ) {
    setTimeout('test('+i+');', i);
  }
}

setTimeout(test2,10);

如果我用如下所示的显式调用替换for()循环,那么它可以正常工作。

setTimeout(function() { test( 0); },  0);
setTimeout(function() { test(10); }, 10);
setTimeout(function() { test(20); }, 20);
setTimeout(function() { test(30); }, 30);
setTimeout(function() { test(40); }, 40);
setTimeout(function() { test(50); }, 50);
setTimeout(function() { test(60); }, 60);
setTimeout(function() { test(70); }, 70);
setTimeout(function() { test(80); }, 80);
setTimeout(function() { test(90); }, 90);

有什么区别?有什么方法可以让for循环生成的setTimeouts在greasemonkey中工作吗?

2 个答案:

答案 0 :(得分:3)

因为当setTimeout为了执行函数而触发时,字符串被唤醒,循环运行它的过程并且i位于循环的最后一个值。

要为每次调用setTimeout冻结i的值,您需要在函数闭包中捕获它,如下所示:

function test2() {
  for( var i = 0; i < 100; i+= 10 ) {
    setTimeout(function(val) {
        return(function() {test(val);});
    } (i), i);
  }
}

这样做的好处是可以去除setTimeout参数中的eval。

答案 1 :(得分:1)

您必须将i的值复制到本地范围的变量中:

function test2() {
  for( var i = 0; i < 100; i+= 10 ) {
    (function(i){
        setTimeout('test('+i+');', i);
    })(i);
  }
}

旁注:您应该将匿名函数传递给setTimeout,而不是传递一个字符串(将被评估)。这种方式要快得多:

function test2() {
    for( var i = 0; i < 100; i+= 10 ) {
        (function(i){
            setTimeout(function(){
                test(i);
            }, i);
        })(i);
    }
}