【问题标题】:Javascript For-Loop issueJavascript For循环问题
【发布时间】:2012-11-10 14:39:30
【问题描述】:

我试图以 200 毫秒的间隔单击每个项目,我编写了以下脚本,但是 For 循环似乎存在问题。有人请告诉我你认为它有什么问题。

function clickLink(elm) {
    var evt = document.createEvent('MouseEvents');
    evt.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
    elm.dispatchEvent(evt);
}

function sel() {
    elms = document.getElementsByClassName('uItem');
    var inputs= elms;
    var howbig= elms.length;
    console.log(howbig);

    for (var i=250;i<elms.length;i++)
    {
        setTimeout(clickLink(inputs[i]),200)
    };

页面上有 1400 个 uItem 。

【问题讨论】:

  • 运行时会发生什么?是什么让您得出 for 循环是问题的结论?
  • @JaniHartikainen 在等式中有或没有 for 循环,所有发生的事情都是我得到一个 sais 1400 的控制台消息(页面上 UItem 的数量但没有被点击。
  • 您将函数的返回值传递给 setTimeout 而不是函数引用。
  • 你想同时点击所有项目,还是一个一个地点击每个项目,间隔200毫秒?
  • @BAK 我希望每次点击之间有 200 毫秒的暂停

标签: javascript asynchronous for-loop


【解决方案1】:

最干净的解决方案就是让您的 clickLink 函数返回一个函数。

function clickLink(elm) {
    return function() {
        var evt = document.createEvent('MouseEvents');
        evt.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
        elm.dispatchEvent(evt);
    };
}

如果你想要一个间隔,然后错开计时器:

var start = 250
for (var i=start;i<elms.length;i++) {
    setTimeout(clickLink(elms[i]), 200 * (i - start))
}

或者放弃循环,使用setInterval

var i = 250,
    len = inputs.length;
var itvl = setInterval(function() {
    clickLink(elms[i++]);
    if (i >= len)
        clearInterval(itvl);
}, 200);

然后回到你原来的clickLink函数

【讨论】:

  • 这是返回绑定函数的显式方式,正如我在回答中提到的那样。我对异步操作的相对复杂的登陆功能(在我的情况下是 AJAX,但控制流程是相同的)的经验是,当我依靠 bind 来做同样的事情时,我发现代码更干净。使用该调用可以在评估参数时变得明确,并且更容易保持头脑清醒。
  • 您需要更好地调整您的200 * i 间隔,请参阅我的回答的UPDATE 3
  • @Nelson:是的,好点子。我忘了起点。
  • +1 为您的解决方案和反馈给其他解决方案。
【解决方案2】:

ì 的范围存在问题,延迟需要与 i 相应地增加以具有 200 毫秒的间隔,以及 setTimeout 期望函数作为参数这一事实。

    for (var i=250;i<elms.length;i++)
    {
        setTimeout((function() {
            var j = i // keep i as j in this closure
            return function() { // return the right function
                clickLink(inputs[j])
            }
        })(),200 * i) // set the delay depending on i
    };

【讨论】:

  • 匿名函数内部i的值不是循环变量的i,因为匿名函数执行时循环不在作用域内。除非i 在全局范围内(颤抖),否则它的值将是null
  • 是什么让你说我不在范围内?
  • i 绝对在匿名函数的可达范围内。
【解决方案3】:
function sel() {
  var elems = document.getElementsByClassName('uItem');
  for (var i = 0; i < elems.length; i += 1) {
    setTimeout(function (el) {
      return function () {
        clickLink(el);
      };
    }(elems[i]), i * 1000);   
  };
}

演示:http://jsfiddle.net/4hYWy/


小提示:我不喜欢这些结构(计算示例中的延迟)。但它与原始代码非常接近,我选择的解决方案可能会使其过于复杂。

【讨论】:

  • 我在回答中提到您可以明确地制作绑定函数。这个答案是一个如何完成的例子,一个函数返回一个函数。
【解决方案4】:

如果您使用的是最新版本的 JavaScript,最简单的方法是使用 bind。这将创建一个函数对象,其中绑定了参数,这些参数在调用 bind 时计算,而不是稍后。因此,您想要的行是:

setTimeout( clickLink.bind( null, inputs[i] ),  /* delay expression here */ )

您可以显式地创建绑定函数,但它很丑陋,并且只有在您强烈要求支持旧的 JavaScript 解释器时才应该这样做。

【讨论】:

  • 我喜欢.bind 解决方案,但是你绑定了函数的this 值,所以你需要在inputs[i] 之前传递一个不同的值作为第一个参数,或者您需要将clickLink 函数更改为使用this 而不是elm
  • 在本例中,这无关紧要,因为this 没有在clickLink 中使用。不过,总的来说,我同意你必须注意你得到的this 的价值。
  • 我同意,this 没有被使用,这就是问题所在。 elm 参数将是 undefined,因为没有传递任何内容,也没有绑定任何内容。
  • 哦,你是对的。脑袋放屁。固定的。我已经习惯了显式地构造绑定函数,所以我的脑海中出现了bind 的签名错误。
【解决方案5】:

此代码等待 200 毫秒,执行回调并准备 200 毫秒后执行的新回调。 第二个回调在sel调用后400ms执行,以此类推。

    var callback = function(i) {
        return function() {
            if (i < elms.length) {
                clickLink(inputs[i]);
                setTimeout(callback(i + 1), 200);
            }
        };
    };
    setTimeout(callback(250), 200);

【讨论】:

  • 大多数其他答案乘以200 * i,因此它们不会同时执行。但是看看你返回的函数。您正在立即调用它,因此它永远不会传递给setTimeout。您需要从返回的函数中删除尾随 ()
  • +1 因为我确实喜欢这个解决方案。但是还有一个问题。 i 的增量是在与.length 比较之后完成的,所以在最后一次调用时,i 将等于elms.length,这意味着inputs[i] 将是undefined。您也应该将clickLink(inputs[i]) 设为有条件的。
【解决方案6】:

你这样做的方式不会让你每 200 毫秒点击一次,你将几乎同时注册所有点击(循环最后一次迭代 1000 个项目的时间,几乎是立即)所以你基本上会得到你的 1000 + 大约。单击事件几乎同时触发,我认为这可能会导致问题,因此要确保它们在大约 200 毫秒内执行一次。这样做:

window.i=250;
for (var i=250;i<elms.length;i++)
{
setTimeout(function() {
   clickLink(inputs[window.i]);
   window.i++;
},200 * (i-249)); //Here we make sure events are spaciated 200ms each approx.
};

更新: 更新了匿名函数以传递 i 以避免在 cmets 中出现关闭问题。

更新 2:在匿名函数中添加了 window.i 以解决 i 范围问题

更新 3:更新了我上面的代码以修复触发每个点击事件的时间间隔,我们使用的是 200 * i,但只有在 i 从零开始时才正确,这里是从 250 开始,所以正确的格式是 200 * (i-249)

尽管有一些 cmets,i 范围问题已使用 window.i 解决,请参阅此处的测试 https://tinker.io/98fd4/1

【讨论】:

  • 向函数添加参数也不起作用,因为当setTimeout 调用它的参数函数时,它没有参数。因此i 将永远是null
  • 只有在持续时间之后通过i才能添加参数。此外,在某些浏览器中无法将参数传递给回调。
  • 感谢您发现范围问题,我已经进行了更新以使用匿名函数中的全局变量来缓解该问题。
  • 使用window.i 是您想要的相反方向。您需要将范围更多本地化,而不是更少。
  • window.i 不会失败,因为它无法运行。它失败了,因为为此目的使用全局会导致代码不可靠,因为践踏了最基本的封装原则。
【解决方案7】:

没有范围问题或计算正确的间隔。

function sel() {
    var elms = document.getElementsByClassName('uItem'),
        i = -1,
        I = elms.length,

        rec = function () {
            if ( i++ < I ) {
                clickLinks(elms[i]);
                setTimeout(rec, 200);
            }
        };

    rec();
}

http://jsfiddle.net/joplomacedo/cqfxH/

【讨论】:

    猜你喜欢
    • 2020-09-15
    • 2012-09-20
    • 1970-01-01
    • 1970-01-01
    • 2011-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多