一文读懂防抖和截流

一文读懂防抖和截流

本篇文章梗概:

什么是防抖和节流?他们有什么区别?分别如何实现?

什么是防抖和节流?防抖和节流,都是开发过程中防止函数多次调用的方式。我现在写的主要是前端开发中的防抖和节流的介绍。

什么是防抖?防抖,顾名思义,防止抖动,以免把一次事件误认为多次,敲键盘就是一个每天都会接触到的防抖操作。

想要了解一个概念,必先了解概念所应用的场景。在 JS 这个世界中,有哪些防抖的场景呢?

1. 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求,需要防抖。2. 调整浏览器窗口大小时,resize 次数过于频繁,造成计算过多,此时需要一次到位,就用到了防抖。3. 文本编辑器实时保存,当无任何更改操作一秒后进行保存。

什么是节流?节流,顾名思义,控制水的流量。控制事件发生的频率,如控制为1s发生一次,甚至1分钟发生一次。与服务端(server)及网关(gateway)控制的限流 (Rate Limit) 类似。

scroll 事件,每隔一秒计算一次位置信息等浏览器播放事件,每个一秒计算一次进度信息等input 框实时搜索并发送请求展示下拉列表,没隔一秒发送一次请求 (也可做防抖)

防抖和节流的区别防抖:防止抖动,单位时间内事件触发会被重置,避免事件被误伤触发多次。代码实现重在清零 clearTimeout。节流:控制流量,单位时间内事件只能触发一次,如果服务器端的限流即 Rate Limit。代码实现重在开锁关锁 timer=timeout; timer=null

防抖和节流的代码实现

防抖的实现

/**

* 防抖(debounce)防止抖动:触发高频事件后 n 秒内函数只会执行一次,如果 n 秒内高频事件再次被触发,则重新计算时间。

* @param {*} func 调用用的函数,function

* @param {*} wait 等待的时间,单位ms

* @param {*} immediate 当immediate为true时,第一次调用该函数的时候,就调用func函数;false表示超时之后再调用

*/

export function debounce(func, wait, immediate) {

let timer;

// 通过闭包缓存一个定时器 id

return function () {

// 将 debounce 处理结果当作函数返回

// 触发事件回调时执行这个返回函数

let context = this;

// 把上下文的this对象保存下来,因为下面的apply要使用

let args = arguments;

console.log(context,"90909090")

// ...args:使用es6的rest运算符,把逗号隔开的值序列组合成一个数组:如test(1,2,3,4) ==> args:[1,2,3,4]

// 符合apply(obj,[])

if (timer) clearTimeout(timer);

//关键点 防抖重在清零

// 如果已经设定过定时器就清空上一次的定时器,clearTimeout取消延迟执行的代码块

if (immediate) {

// 如果immediate为true,那么立马调用该函数

var callNow = !timer;

timer = setTimeout(() => {

timer = null;

}, wait);

if (callNow) func.apply(context, args);

// 过了设定的时间,才执行传过来的函数

} else {

// 开始设定一个新的定时器,定时器结束后执行传入的函数 fn

timer = setTimeout(function () {

func.apply(context, args);

}, wait);

}

};

}

节流的实现

//这个是在上一个函数上的改进,加强版节流函数 throttle

//如下,新增逻辑在于当前触发时间和上次触发的时间差小于时间间隔时,设立一个新的定时器,相当于把 debounce 代码放在了小于时间间隔部分。

export function throttle(fn, wait) {

let timer = null;

let prev = new Date();

return function () {

let nowTime = new Date();

// 获取当前时间,转换成时间戳,单位毫秒

let context = this;

clearTimeout(timer);

// ------ 新增部分 start ------

// 判断上次触发的时间和本次触发的时间差是否小于时间间隔

// 如果小于,则为本次触发操作设立一个新的定时器

// 定时器时间结束后执行函数 fn

if (nowTime - prev > wait) {

fn.apply(context, arguments);

prev = new Date();

// ------ 新增部分 end ------

} else {

timer = setTimeout(() => {

fn.apply(context, arguments);

}, wait);

}

};

}

延伸:es6 rest argumentes6 引入了rest参数(形式:...变量名),用于获取函数的多余参数,这样就不需要使用arguments对象了。rest参数搭配的变量是一个数组,该变量将多余的参数放入数组中。

var fun = (...item)=>{

console.log(item)

}

fun(11,22,33,44,55,66,77,88,99)

apply方法apply:方法能劫持另外一个对象的方法,继承另外一个对象的属性.

Function.apply(obj,args)方法能接收两个参数obj:这个对象将代替Function类里this对象args:这个是数组,它将作为参数传给Function(args-->arguments)

用法:将数组作为函数参数例子:Math.max后面可以接任意个参数,最后返回所有参数中的最大值。一般这样做,需要取数组里面每个值,毕竟很多传参是接受对象的

function getMax(arr){

var arrLen=arr.length;

for(var i=0,ret=arr[0];i

ret=Math.max(ret,arr[i]);

}

return ret;

}

用apply就简单很多

function getMax2(arr){

return Math.max.apply(null,arr);

}

这样看来`...args`,使用es6的rest运算符,把逗号隔开的值序列组合成一个数组:如test(1,2,3,4) ==> args:[1,2,3,4],`apply`把数组转成对象。这里args是继承的传入函数的参数。

防抖代码,关键就是,对象转换两遍,clearTimeout取消延迟执行的代码块,清零。

其实不管防抖和截流还有很多其他方法,都可以用loadash类库去实现,不用自己苦哈哈的写。

相关推荐