如何将未指定数量的参数传递给 setInterval

How to pass unspecified number of parameters to setInterval

我需要创建一个间隔包装器来跟踪它是否已被清除。

传递给间隔回调的参数数量应该是可变的。所以这是我用来测试它的代码(不工作):

function MyInterval() {
  var id = setInterval.apply(this, arguments); // NOT VALID!!
  this.cleared = false;
  this.clear = function() {
    this.cleared = true;
    clearInterval(id);
  };
}

var x = 2;
var y = 3;
var fn = function() {
  x = x + y;
  console.log(x);
};
var interval = new MyInterval(fn, 5000, x, y);

在对 setInterval 的调用中,this 必须引用全局对象,因此您需要在构造函数中使用 window 而不是 this

var id = setInterval.apply(window, arguments);
// here -------------------^

(或者在宽松模式下你可以使用 undefinednull。)

然后它就可以工作了,至少在 setInterval 是一个真正的 JavaScript 函数并因此具有 apply:

的浏览器上

function MyInterval() {
  var id = setInterval.apply(window, arguments);
  this.cleared = false;
  this.clear = function() {
    this.cleared = true;
    clearInterval(id);
  };
}

var x = 2;
var y = 3;
var fn = function() {
  x = x + y;
  log(x);
};
var interval = new MyInterval(fn, 500, x, y);
setTimeout(function() {
  interval.clear();
}, 3000);

function log(msg) {
  var p = document.createElement('p');
  p.appendChild(document.createTextNode(msg));
  document.body.appendChild(p);
}

注意,不过,主机提供的函数只需要可调用,它们不需要继承自Function.prototype 所以他们 required/guaranteed 没有 apply。现代浏览器确保它们这样做,但早期的浏览器(例如 IE8)没有。我无法说明 applysetInterval.

上的支持程度如何

如果您需要支持可能没有它的浏览器,只需使用您自己的功能:

function MyInterval(handler, interval) {
  var args = Array.prototype.slice.call(arguments, 2);
  var tick = function() {
    handler.apply(undefined, args);
  };
  var id = setInterval(tick, interval);
  this.cleared = false;
  this.clear = function() {
    this.cleared = true;
    clearInterval(id);
  };
}

这还有一个优点,即使在 setInterval(相当旧的)上不支持额外参数的浏览器也能正常工作。

示例:

function MyInterval(handler, interval) {
  var args = Array.prototype.slice.call(arguments, 2);
  var tick = function() {
    handler.apply(undefined, args);
  };
  var id = setInterval(tick, interval);
  this.cleared = false;
  this.clear = function() {
    this.cleared = true;
    clearInterval(id);
  };
}

var x = 2;
var y = 3;
var fn = function() {
  x = x + y;
  log(x);
};
var interval = new MyInterval(fn, 500, x, y);
setTimeout(function() {
  interval.clear();
}, 3000);

function log(msg) {
  var p = document.createElement('p');
  p.appendChild(document.createTextNode(msg));
  document.body.appendChild(p);
}

您可能想使用新的 ES2015 传播运算符:

var id = setInterval(...arguments);

...但请注意,如果您转译(现在您必须转译),它最终会成为 apply 调用,因此您会遇到是否 apply支持。

我建议您将 "options" 参数传递给超时。

var MyInterval = (function(window) {
  return function(callbackFn, timeout, options) {
    var id = setInterval.apply(window, arguments);
    this.cleared = false;
    this.clear = function() {
      this.cleared = true;
      clearInterval(id);
    };
  }
}(window));

var fn = function(opts) {
  opts.x += opts.y;
  console.log('x = ', opts.x);
};
var opts = {
  x: 2,
  y: 3
};
var ms = 5000;

var interval = new MyInterval(fn, ms, opts);

// Bootstrap a custom logger. :)
console.log = function() {
  var logger = document.getElementById('logger');
  var el = document.createElement('LI');
  el.innerHTML = [].join.call(arguments, ' ');
  logger.appendChild(el);
  logger.scrollTop = logger.scrollHeight;
}
body{background:#7F7F7F;}h1{background:#D7D7D7;margin-bottom:0;padding:0.15em;border-bottom:thin solid #AAA;color:#444}#logger{height:120px;margin-top:0;margin-left:0;padding-left:0;overflow:scroll;max-width:100%!important;overflow-x:hidden!important;font-family:monospace;background:#CCC}#logger li{list-style:none;counter-increment:step-counter;padding:.1em;border-bottom:thin solid #E7E7E7;background:#FFF}#logger li:nth-child(odd){background:#F7F7F7}#logger li::before{content:counter(step-counter);display:inline-block;width:1.4em;margin-right:.5em;padding:.25em .75em;font-size:1em;text-align:right;background-color:#E7E7E7;color:#6A6A6A;font-weight:700}
<h1>Custom HTML Logger</h1><ol id="logger"></ol>

我创建了实用函数而不是构造函数来解决您的问题。

function Wrapper(delay) {
  var isCleared,
      intervalId,
      intervalDelay = delay || 5e3; // default delay of 5 sec

  function clear() {
    if (!isCleared) {
      console.log('clearing interval');
      isCleared = true;
      clearInterval(intervalId);
    }
  }

  function setUpInterval(callback){
    var params = [].slice.call(arguments, 1);
    if (!callback) {
      throw new Error('Callback for interval expected');
    }

    params.unshift(intervalDelay);
    params.unshift(callback);

    intervalId = setInterval.apply(null, params);
  }

  return {
    setUp : setUpInterval,
    clear : clear
  }
}

function intervalCallback() {
  console.log([].slice.call(arguments).join(','));
}


var wrapper = Wrapper(1e3); // create wrapper with delay for interval


console.log('test case 1');
wrapper.setUp(intervalCallback, 'params', 'to', 'callback');

// call clear interval after 10sec
setTimeout(function() {
  wrapper.clear();
}, 10e3);

希望对您有所帮助。