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

Javascript 将多个操作绑定到事件侦听器时会出现性能问题,javascript,events,ecmascript-6,listener,addeventlistener,Javascript,Events,Ecmascript 6,Listener,Addeventlistener,让我们使用滚动侦听器示例 如果我们有太多的元素(例如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()
  // 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

我不确定这将如何在幕后编译,以及哪种方法更好。

第一种方法更可取,因为它只是一个函数。在第二种格式中,需要调用所有20个滚动处理程序,即使其中只有一个中的
if
语句实际匹配。此外,滚动事件是众所周知的密集,因为它可以发射很多

但这一单一功能可以不费吹灰之力加以改进。您可以使用对象映射来代替包含二十个分支(二十个元素)的大规模
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
语句。

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

但这一单一功能可以不费吹灰之力加以改进。您可以使用对象映射来代替包含二十个分支(二十个元素)的大规模
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
语句。

因为danwellman的响应表明
滚动
事件将以每秒几次的速度触发。这可能会导致资源、内存分配等方面的开销。因此,我建议您使用节流功能:

     /* 
      * 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.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

danwellman的响应表明,
滚动
事件将以每秒几次的速度触发。这可能会导致资源、内存分配等方面的开销。因此,我建议您使用节流功能:

     /* 
      * 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.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

第一个可能更好,我不认为添加多个事件侦听器会有帮助,请尝试
console.dir(e)在事件侦听器中,查看如何使用详细的事件属性来处理多个条件。第一个。特别是当你能够巧妙地处理条件和共享计算时。第一个可能会更好。我不认为添加多个事件侦听器会有帮助,请尝试
console.dir(e)在事件侦听器中,查看如何使用详细的事件属性来处理多个条件。第一个。特别是当你能聪明地处理条件和共享计算时。这看起来真的很有趣,我在考虑多个侦听器,因为我认为我可以消除其中的一些。因为也许你知道,我们可以有一些不需要一直检查的条件。这看起来真的很有趣,我在考虑多个侦听器,因为我认为我可以消除其中的一些。因为也许你知道,我们可以有一些不需要一直检查的条件。是的,这是必须的,我已经在使用lodash库来进行去盎司和节流方法。但我更担心的是2事件侦听器是否会将内存消耗增加2,而不是将所有逻辑放在一个事件侦听器中(如果可能的话)啊,这是必须的,我已经在使用lodash库来实现去盎司和节流方法。但我更担心的是,如果可能的话,2事件侦听器是否会将内存消耗增加2,而不是将所有逻辑放在一个事件侦听器中