zoukankan      html  css  js  c++  java
  • “如何稀释scroll事件”引出的问题

    背景:我在segmentfault提了个问题如何稀释onscroll事,问题如下:

    面试时问到这个问题,是这样的:
        面试官问一个关于滚动到某个位置的时候出现一个顶部的导航栏,答完之后,她接着问一滚动onscroll就会执行很多很多次,如何稀释它?为了确定她说的是“稀释”,我让她重复了遍,我给出的解决方法是,用一个变量,在事件处理的时候让它自增,判断达到一定大小就执行一次实际的事件:

    var i = 0;    // 累积变量
    window.onscroll = function(){
        i++;
        if(i%500==0){
            // 执行实际的事件
        }
    }
    
    • 她并不满意,问最后如何释放这个变量?
    • ……
    • 接着她说:“我要的是稀释onscroll的执行次数,而不是这个(我所指的实际的事件)的执行次数。”
    • 我很是不解,鼠标一滚动就触发这个事件,如何能减少它的执行次数,如何稀释它?难道动态绑定/解绑事件,如何操作?

        网上都没有找到相关类型的问题,这个问题算不算变态级的面试题?如果不是,请给出解决方案,先谢谢了。

    第一个回答认为这是函数节流,顿时恍然大悟,纳为答案,这是我和该名词的初次接触,本以为就这样结束了,没想到接下来又吸引了一大波程序员的眼球。

    基本地,指出使用 throttle 和 debounce 两种方式:

    throttle(译:节流阀):就是函数节流的意思,控制函数调用的频度,固定时间间隔执行,即连续的调用中,不管频度如何,只间隔固定时间(或大于该时间,这时频度较低)执行一次,不是问题中的“稀释”。

    debounce(译:防反跳?):就是去抖的意思,空闲控制,在一定空闲时间间隔内的调用不予实现,一个简单的实现如下:

     1 var timer = null;
     2 document.addEventListener('mousemove', function () {
     3         if (timer) {
     4             clearTimeout(timer)
     5         }
     6         timer = setTimeout(function(){
     7             console.log("mousemove");
     8         }, 100);
     9     }
    10 );

    主要应对高度频发的调用,100的意思不是100ms执行一次,而是当调用间隔时间不超过100ms,即鼠标移动速度过快的话,console.log()会一直不被执行,除非移动间隔时间大于100ms,用网友bumfod的话,「函数节流让一个函数只有在你不断触发后停下来歇会才开始执行,中间你操作得太快它直接无视你。」,不是问题中的“稀释”。

    综上,诚然 throttle 和 debounce 都能很好地解决性能问题,二者稀释的是业务逻辑的执行次数,但都不是问题所要求的,这时我就觉得这个问题有点牵强了,因为不管怎样,不管有没有显性地定义 scroll 事件,浏览器都会触发 scroll 事件的,差别在于有没有 callback,有没有要执行的东西而已。

    如果非要“减少scroll 的执行次数”,这里有一位和我不谋而合的网友代码,通过setTimeout,执行一次再延时重新绑定事件监听器,这种方法稀释的是回调的执行次数

     1 var cb = {
     2   onscroll:function() {
     3     console.log("scrolling");
     4     window.removeEventListener("scroll", cb.onscroll, false);   // 这里移除事件监听器
     5     setTimeout(function() {
     6         console.log("DONE");
     7         window.addEventListener("scroll", cb.onscroll, false);
     8     }, 200);    // 200ms后重新绑定事件监听器
     9   }
    10 };
    11 window.addEventListener("scroll", cb.onscroll, false);

    还有同学引出了阻塞渲染、影响页面UI响应等的问题,

     

    其他:

    框架辅助 _debounce(underscore.js 里的 debounce 函数)

     1 /**
     2  * [debounce description]
     3  * @param  {[type]} func      [回调函数]
     4  * @param  {[type]} wait      [等待时长]
     5  * @param  {[type]} immediate [是否立即执行]
     6  * @return {[type]}           [description]
     7  */
     8 _.debounce = function(func, wait, immediate) {
     9     var timeout, args, context, timestamp, result;
    10 
    11     var later = function() {
    12         var last = _.now() - timestamp;
    13 
    14         //小于wait时间,继续延迟wait-last执行later,知道last >= wait才执行func
    15         if (last < wait && last > 0) {
    16             timeout = setTimeout(later, wait - last);
    17         } else {
    18             timeout = null;
    19             if (!immediate) {
    20                 result = func.apply(context, args);
    21 
    22                 if (!timeout) context = args = null;
    23             }
    24         }
    25     };
    26 
    27     return function() {
    28         context = this;
    29         args = arguments;
    30         timestamp = _.now();
    31         //是否立即执行
    32         var callNow = immediate && !timeout;
    33 
    34         if (!timeout) timeout = setTimeout(later, wait);
    35 
    36         if (callNow) {
    37             result = func.apply(context, args);
    38             context = args = null;
    39         }
    40 
    41         return result;
    42     };
    43 };
    underscore.js 的 debounce

    typeahead 的 throttle 实现源码:

     1 throttle: function(func, wait) {
     2     var context, args, timeout, result, previous, later;
     3     previous = 0;
     4     later = function() {
     5         previous = new Date();
     6         timeout = null;
     7         result = func.apply(context, args);
     8     };
     9     return function() {
    10         var now = new Date(),
    11             remaining = wait - (now - previous);
    12         context = this;
    13         args = arguments;
    14         if (remaining <= 0) {   //如果大于间隔时间(wait)
    15             clearTimeout(timeout);
    16             timeout = null;
    17             previous = now;
    18             result = func.apply(context, args);
    19         } else if (!timeout) {  //小于,延时调用later
    20             timeout = setTimeout(later, remaining);
    21         }
    22         return result;
    23     };
    24 },
    typeahead.js 的 throttle

    如有纰漏,恳请指出,共同进步,谢谢^_^

  • 相关阅读:
    PJzhang:CVE-2020-1472微软NetLogon权限提升漏洞~复现
    PJzhang:vulnhub靶机sunset系列SUNSET:DECOY
    PJzhang:vulnhub靶机sunset系列SUNSET:TWILIGHT
    PJzhang:vulnhub靶机sunset系列SUNSET:SUNRISE
    PJzhang:vulnhub靶机sunset系列SUNSET:MIDNIGHT
    PJzhang:vulnhub靶机sunset系列SUNSET:SOLSTICE
    PJzhang:whatweb指纹扫描工具样例
    HTTP状态码解读
    【并发】线程与进程的区别
    MYSQL之性能优化 ----MySQL性能优化必备25条
  • 原文地址:https://www.cnblogs.com/xzhang/p/4145697.html
Copyright © 2011-2022 走看看