简述防抖与节流(含完整模拟实例)

332 阅读4分钟
  • 文章看似很长,实则防抖与节流内容类似,理解其一便知其二
  • 代码不多,耐心跟着敲一遍,基本能理解防抖和节流思想

防抖节流对比

防抖节流
概念连续的事件,只最后一次触发回调
(如:停止滚动滑动条时,触发一次回调)
连续的事件,间隔一段时间执行一次回调
(如:滚动滚动条时,每隔一段事件触发一次回调)
应用场景1、避免用户快速点击按钮,持续发送请求
2、搜索框持续输入
3、手机号、邮箱验证
4、鼠标的mousemove、mouseover
1、滚动加载,加载更多
2、搜索框联想功能

防抖

一、原理

事件触发n秒后才能再次发起请求

二、需求场景实例

例:用防抖模拟解决输入框多次发起请求问题

防抖.gif

三、实现思路

  • 1、实现原始输入
<input type="text"/>
//js
var int = document.querySelector("input");
    int.oninput = function(){
        console.log(this.value)  //模拟业务逻辑
    }
  • 2、若触发事件,给定一个setTimeOut定时器做延迟触发
  • 3、初始化定时器为null
  • 4、每次点击,清除且重置定时器
  • 5、最后一次点击,即n秒后执行业务逻辑
var int = document.querySelector("input");
var time = null;  //初始化定时器
    int.oninput = function(){
        if(time != null){  //判断是否已存在定时器
            clearTimeout(time)  //清空定时器
        }
        time = setTimeout(() => {
            console.log(this.value);   //模拟业务逻辑
        }, 500);
    }

效果如下:

防抖2.gif

四、防抖进阶优化

  • 痛点:以上方法将防抖函数和业务逻辑放到一起,代码块不优雅,后期难以维护
  • 解决:将防抖函数与业务逻辑单独封装
实现思路

1、封装业务逻辑

//业务逻辑
function task(){
    console.log(this.value);  //模拟业务逻辑
}

2、封装防抖函数

  • 封装一个名为debounce,且返回值为函数的防抖函数,来替代oninput后承接的内容。
  • 因为返回值为函数,所以用闭包思想来实现封装
function debounce(){
    return function(){}
 }
  • 防抖函数里传入业务逻辑fn()和延迟时间delay
  • 优化全局变量time,把变量time作为私有变量放入debounce函数中
function debounce(fn,delay){
      let time = null
      return function(){
        //此处是防抖逻辑
       }
  }

3、组装业务逻辑函数和防抖函数(完整代码)

var int = document.querySelector("input");
int.oninput = debounce(task,500)

//业务逻辑
function task(){
    console.log(this.value);
}
//防抖函数
function debounce(fn, delay){
    var time = null;
    return function(){
        if(time != null){
            clearTimeout(time)
    }

    time = setTimeout(() => {
            fn.call(this) // 注:此处需改变this指针,否则业务逻辑中的this指向window
        }, delay);
    } 
}

节流

一、原理

控制执行次数,n秒内多次触发事件只有1次生效

二、需求场景实例

例:用节流模拟解决滑动浏览器多次请求的问题

节流1.gif

三、实现思路

  • 1、实现原始输入
<style>
    body{
        height: 10000px;
    }
</style>
//js
window.onscroll = function(){  
    console.log('触发')
}

2、设置标记变量,限制值为true时,进入计时器计时并执行业务逻辑 3、设置setTimeOut,定时器在规定时间后才能再次触发

var flag = true;
var time = null;

window.onscroll = function(){  
    if(flag){   //标记变量为true时开始计时
        time = setTimeout(() => {
            console.log('触发')
            flag = true;
        }, 500);
    }
    flag = false; //n秒内flag值为false,不触发计时器
}

效果如下:

节流2.gif

四、节流进阶优化

  • 痛点:以上方法将节流函数和业务逻辑放到一起,代码块不优雅,后期难以维护
  • 解决:将节流函数与业务逻辑单独封装
实现思路

1、封装业务逻辑

function task(){
    console.log('触发');
}

2、封装防抖函数

  • 封装一个名为throttle,且返回值为函数的节流函数,来替代window.onscroll后承接的内容。
  • 因为返回值为函数,所以用闭包思想来实现封装
function throttle(){
    return function(){
    }
}
  • 节流函数里传入业务逻辑fn()和延迟时间delay
  • 初始化标记变量flag,值为true
  • 优化全局变量flag,time,把变量time作为私有变量放入throttle函数中
function throttle(fn,delay){
    var flag = true;
    var time = null;
    return function(){
    }
}

3、组装业务逻辑函数和防抖函数(完整代码)

 window.onscroll = throttle(task, 500)
 
//业务逻辑
function task(){
    console.log('触发');
}
//节流函数
function throttle(fn,delay){
    var flag = true;
    var time = null;
    return function(){
        if(flag){
            time = setTimeout(() => {
                fn.call(this)// 注:此处需改变this指针,否则业务逻辑中的this指向window
                flag = true;
            }, delay)
        }
        flag = false;
    }
}