将多个操作绑定到事件侦听器时,性能会出现问题

时间:2017-11-24 15:44:31

标签: javascript events ecmascript-6 listener addeventlistener

让我们使用scroll侦听器示例。

如果我们有太多元素(例如20个),那些元素需要在某个滚动参数上做某事。

window.addEventListener('scroll', function(e) {
  // condition 1 -> if true doSomething()
  // condition 2 -> if true doSomething2()
  // condition 3 -> if true doSomething3()
  //...etc
});

我们将有一个超过20个条件的听众。这样好吗? 或者最好有不同的滚动事件监听器。

window.addEventListener('scroll', function(e) {
  // condition 1 -> if true doSomething()
});

window.addEventListener('scroll', function(e) {
  // condition 2 -> if true doSomething2()
});

window.addEventListener('scroll', function(e) {
  // condition 3 -> if true doSomething3()
});  // ...etc

我不确定这将如何在幕后编辑,哪一个是更好的方法。

2 个答案:

答案 0 :(得分:2)

第一个更好,因为它只是一个功能。在第二种格式中,需要调用所有20个滚动处理程序,即使其中只有一个实际匹配的if语句也是如此。此外,已知滚动事件是密集的,因为它可以激活很多

但是可以毫不费力地改进单一功能。您可以使用对象映射来代替具有20个分支(对于20个元素)的大量if语句,而不是像这样:

window.addEventListener('scroll', function(e) {
    const handlers = {
      elementId1: function() {
          // handle element 1
      },
      element2Id: function() {
          // handle element 2
      },
      etc...
    };

    handlers[e.target.id]();
});

在这种情况下,您只需确保相关DOM元素具有正确的id属性。这避免了条件逻辑。关键不在于这个特定的代码是最好的,只是有办法避免大if个语句。

答案 1 :(得分:1)

由于danwellman的反应表明scroll事件将被发射数次/毫秒。这可能会导致资源,内存分配等方面的开销。所以我建议您使用节流功能:

     /* 
      * Returns a function, that, when invoked, will only be triggered at most once
     * during a given window of time. Normally, the throttled function will run
     * as much as it can, without ever going more than once per `wait` duration;
     * but if you'd like to disable the execution on the leading edge, pass
     * `{leading: false}`. To disable execution on the trailing edge, ditto.
    */
   var throttle = function(func, wait, options) {
        var context, args, result;
        var timeout = null;
        var previous = 0;
        if (!options) options = {};
        var later = function() {
            previous = options.leading === false ? 0 : Date.now();
            timeout = null;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
        };
        return function() {
            var now = Date.now();
            if (!previous && options.leading === false) previous = now;
            var remaining = wait - (now - previous);
            context = this;
            args = arguments;
            if (remaining <= 0 || remaining > wait) {
            if (timeout) {
                clearTimeout(timeout);
                timeout = null;
            }
            previous = now;
            result = func.apply(context, args);
            if (!timeout) context = args = null;
            } else if (!timeout && options.trailing !== false) {
            timeout = setTimeout(later, remaining);
            }
            return result;
        };
    }

现在添加普通滚动事件:

window.addEventListener('scroll', function(e) {
  console.log("scroll event")
});

和受限制的只是为了比较:

window.onscroll= throttle( function(e) { console.log("throttled scroll event") }, 1000 * 5, {leading:false})

这将被发射不少于每5秒,所以在控制台中你会看到它们之间的差异:

(126) scroll event
throttled scroll event
(15) scroll event
throttled scroll event