5分钟带你掌握js的防抖节流

349 阅读3分钟

前言

这是我参与更文挑战的第3天,活动详情查看更文挑战

函数防抖和节流是优化高频率执行js代码的一种手段。作为前端开发是必须要知道的,当然,也是面试的常考点之一,

防抖函数

原理:在事件被触发n秒后,再去执行回调函数。如果n秒内该事件被重新触发,则重新计时。结果就是将频繁触发的事件合并为一次,且在最后执行

案例

需求:输入框的结果是在我们键盘弹起时,在规定的时间内没有输入,就打印结果

let input = document.getElementById('input')
function debounce(delay){
   let timer;
   return function(value,callback){
    clearTimeout(timer)
    timer = setTimeout(function(){
        callback(value)
    },delay)
   }
}
// 为了方便封装,把调试打印的结果以callback的形式传入
function fn(value){
    console.log(value)
}
var debounceFn = debounce(1000)
input.addEventListener('keyup',function(e){ 
    debounceFn(e.target.value,fn)
})

实际应用场景

  • 使用echart时,改变浏览器宽度的时候,希望重新渲染echart的图像,可以使用此函数,提升性能。(虽然echarts里有自带的resize函数)

  • 典型的案例就是模糊搜索:输入结束后n秒才进行搜索请求,n秒内又输入的内容,就重新计时。解决搜索的bug,要在请求之前拿到最后一次输入的结果

节流函数

当持续触发事件的时候,保证一段时间,只调用一次处理函数,(一段时间内,只做一件事情)

实际应用 表单的提交

典型的案例就是鼠标不断点击触发,规定在n秒内多次点击只有一次生效

// 如果不用闭包的话,就会你点击几次,就会执行几次
// 这不是我们想要的,我们想要的是,比如说,时间是一秒,哪怕你手速再快,一秒内,就算你点击了1000次,我也只执行一次
// 使用闭包的话,就会把变量保存在内存之中
function throttle(func,wait){
    let timeOut 
    return function(){
        if(!timeOut){
            timeOut = setTimeout(function(){
                func()
                // 处理完之后就不处理了
                timeOut = null
            },1000)
        }
    }
}
function handle(){
    console.log(Math.random())
}
document.getElementById('thro').onclick = throttle(handle,1000)

两者综合使用的案例

图片懒加载是我们前端开发中最常见的一种优化性能方式。下面这个是使用防抖节流后的案例。

html部分
<img src="" data-src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3205580013,1833705086&fm=26&gp=0.jpg" alt="" />
   
javascript部分
var num = document.getElementsByTagName('img').length
    var img = document.getElementsByTagName('img')
    var n = 0 // 存储图片加载到的位置,避免每次都从第一张图片开始遍历
    var isLoadImg = false // 是否当前容器/页面里的图片已加载完成
    var _clientHeight = document.documentElement.clientHeight // 可视区域高度
    var _scrollTop = document.documentElement.scrollTop || document.body.scrollTop //  滚动条距离顶部高度
    // 监听窗口变化 重新计算可视区域
    function computedClientHeight(){
        _clientHeight = document.documentElement.clientHeight //  可视区域高度
    }
    // 页面载入完毕加载可视区域的图片
    lazyLoad()
    function lazyLoad(){
        // 获取滚动条距离顶部高度
        isLoadImg = n >= num
        _scrollTop = document.documentElement.scrollTop || document.body.scrollTop
        for(var i=n;i < num;i++){
            if(img[i].offsetTop <_clientHeight +_scrollTop) {
                if (img[i].getAttribute('src')==''){
                    img[i].src = img[i].getAttribute('data-src')
                }
                n + 1
            }
        }
    }
    /**
     * 简单的节流函数throttle
     * @params {*}
     * func 要要执行的函数
     * delay 延迟
     * time 在time时间内必须执行一次
     * flag 是否继续触发节流函数(根据需求定义,这里就是想当加载完图片时不需要进行多余的操作
     **/
    function throttle(func,wait,flag){
        let timeOut
        return function(){
            if (flag){
                return
            }
            if(!timeOut) {
                timeOut = setTimeout(function(){
                    timeOut = null
                    func()
                },wait)
            }
        }
    }
/**
* 简单的节流函数throttle
* @params {*}
* func 要要执行的函数
* delay 延迟
* time 在time时间内必须执行一次
* flag 是否继续触发节流函数(根据需求定义,这里就是想当加载完图片时不需要进行多余的操作
**/ 
function debounce(callback,delay) {
    let timer
    return function(arg) {
        clearTimeout(timer)
        timer = setTimeout(function(){
            callback(arg)
        },delay)
    }
}
// 使用了节流函数-实现性能较好的懒加载
window.addEventListener('scroll',throttle(lazyLoad,100,isLoadImg))
// 使用防抖函数,优化不断触发的窗口变化
window.addEventListener('resize',debounce(computedClientHeight,800))