很多网站都会提供一个按钮:用于返回顶部。
这个按钮只会在滚动到距离顶部一定位置的时候才出现-(监听滚动事件,返回当前到顶部的距离)

function showTop() {
    var scrollTop = document.body.scrollTop || document.documentElement.scrollTop
    console.log(scrollTop)
}
window.onscroll = showTop

但是运行的时候存在一个问题:函数默认执行频率太高,按一次键盘的下方向键,函数就执行了9次!
图片说明
实际应用上不需要高频执行反馈,这样会影响浏览器性能,如何优化成为问题。

第一种思路:防抖(debounce)
在第一次触发事件时,不立即执行函数,而给出一个期限值比如200ms
·如果在200ms内没有再次触发滚动事件,就执行函数
·如果在200ms内再次触发了滚动事件,当前计时取消,重新开始计时
效果:短时间能大量触发同一事件,只会执行一次函数。
实现:计时,实现的关键就是setTimeout函数,还需要一个变量来保存计时。考虑维护全局纯净,借助闭包实现

function debounce(fn,delay){
    let timer = null 
    return function(){
        if(timer){
            clearTimeout(timer)  //进入该分支说明在计时过程中又触发了该事件,所以清除定时
            timer = setTimeout(fn,delay) //重新开始计时,实际这句不用写,因为一定会执行计时
        }else{
            timer = setTimeout(fn,delay) //进入该分支说明当前没有在计时,开始计时)
        }
    }
}

然后接着上面的代码 window.onscroll = debounce(showTop,1000)
此时,只有在停止滚动1秒之后,才会打印出滚动条的位置
定义:对于短时间内连续触发的事件,防抖的含义就是让某个时间期限内,事件处理函数只执行一次

        function debounce(fn,delay){
            let timer = null
            return function(){
                if(timer){
                    clearTimeout(timer)
                }
                timer = setTimeout(()=>{
                    //保证可以将参数传入
                    fn.apply(this,arguments)
                }, delay);
            }
        }
        var ipt = document.getElementById('ipt');
        ipt.addEventListener('keyup',debounce(function(e){
            console.log(e.target)
            console.log(ipt.value)
        },1000))

监听一个input输入框keyup事件,解决event参数传参问题使用apply()

第二种思路:节流(throttle)
使用上面的防抖方案来处理问题的结果是:
·如果在限定时间段内,不断触发滚动事件(某个用户按住滚动条不断的拖来拖去),只要不停止触发,理论上就永远不会输出当前距离顶部的距离
怎么处理成为关键:设计一种类似控制阀门一样定期开放的函数,也就是让函数执行一次后,在某个时间段内失效,过了这段时间再重新激活(类似技能冷却时间)

function throttle(fn,delay){
    let valid = true  // 状态位标识当前函数是否处于工作状态
    return function(){
        if(!valid){
            return false // 失效状态,不往下执行
        }
        valid = false //先将标志位处于休息装填,在定时器计时完成之前不执行函数
        setTimeout(() => { 
            fn();
            valid = true; // 进入定时器,执行函数再将阀门打开
            },delay)
    }
}
然后接着上面的代码 window.onscroll = throttle(showTop,1000)

上面代码运行结果是:如果一直拖着滚动条进行滚动,会以1s的时间间隔,持续输出当前位置和顶部距离

//更好理解版本,适配含event参数等使用fn.apply()
        function throttle(fn,delay){
            let timer = null
            return function(){
                if(timer){
                    return 
                }
                timer = setTimeout(()=>{
                    fn.apply(this,arguments)
                    //相比防抖要加这一句才会重复触发不然只执行一次
                    timer = null
                }, delay);
            }
        }

用一句话总结防抖和节流的区别:防抖是将多次执行变为最后一次执行,节流是将多次执行变为每隔一段时间执行

平时开发常遇到的场景:
1.搜索框input事件,例如要支持实时搜索就可以使用节流方案(间隔一段时间就必须查询相关的内容),或者实现输入间隔大于某个值,就当作用户输入完成,开始搜索,具体使用哪种方案需要看业务需求
2.页面resize事件,常用于需要做页面适配的时候。需要根据最终呈现的页面情况进行dom渲染(这种情形一般是使用防抖,因为只需要判断最后一次的变化情况)
https://segmentfault.com/a/1190000018428170