什么是防抖和节流
防抖:动作绑定事件,动作发生后在设置的一定时间后触发,如果在这个时间内,再次发生该动作,就要重新再等待一定时间再触发事件(事件隔一定时间时候还会触发)
节流:在动作绑定事件之后,动作发生后一段时间后触发事件,在这个时间内,如果再次触发这个动作,则会被无视,等到事件执行完毕再重新触发
为什么会用到防抖或者节流呢?
在实际中使用的window 的 resize、scroll,mousedown、mousemove,keyup、keydown,这些事件我们都会常用到
var count = 1;var container = document.getElementById('container');function getUserAction() { container.innerHTML = count++;};container.onmousemove = getUserAction;
上面的js方法,是鼠标移入之后在container容器上输出数字,且数字随着鼠标移动自动加一,在这个简单的函数中我们看到数字会变化很快,但是如果是复杂的请求呢。特别是在业务复杂时,在实际项目中,考虑到入参数据的处理及网络,假设 1 秒触发了 60 次请求,每个回调就必须在 1000 / 60 = 16.67ms 内完成,否则就会有卡顿出现,这样就很容易卡顿。
防抖的实现方式
//第一个实现方式function debounce(func, time) { let timeout; return function () { clearTimeout(timeout) timeout = setTimeout(func, time); }}
使用例子一
container.onmousemove = debounce(getUserAction, 1000); //这样在鼠标移入之后不管怎么移动,在1s内是不会再次触发这个函数的
如果我们在getUserAction
函数中console.log(this)
,在不使用debounce
函数的时候,this
的值为容器,如果使用this则指向了debounce。
//第二个实现方式,改变this的指向function debounce(func, wait) { var timeout; return function () { var _this= this; clearTimeout(timeout) timeout = setTimeout(function(){ func.apply(_this) }, wait); }}//这样this会指向容器
JavaScript 在事件处理函数中会提供事件对象 event,如果我们在getUserAction函数中打印event,可以打印出event,如果在上面的debounce函数中打印,只会打印出undefined。所以我们可以做如下修改:
// 第三次修改function debounce(func, wait) { var timeout; return function () { var context = this; var args = arguments; clearTimeout(timeout) timeout = setTimeout(function(){ func.apply(context, args) }, wait); }}// 在这个方法中用到apply,解决了event输出undefined。
在上面的三个方法修改中,我们发现只会在操作之后到一定时间之后才执行,如果我们有这样一个需求:我们希望方法立即执行,然后到n秒之后再去重新触发执行,我们就可以增加一个immediate参数去判断,如果为true则立即执行(核心方法已经在上面了,后面的扩展时根据实际需求,对方法进行改造)
//对方法进行扩展function debounce(func, wait, immediate) { var timeout; return function () { var context = this; var args = arguments; if (timeout) clearTimeout(timeout); // 如果需要立即执行,则执行下面方法 if (immediate) { // 如果已经执行过,不再执行,没有执行则立即执行 var callNow = !timeout; timeout = setTimeout(function(){ timeout = null; }, wait) if (callNow) func.apply(context, args) } else { timeout = setTimeout(function(){ func.apply(context, args) }, wait); } }}
如果getUserAction是个有返回值的函数怎么办呢?但是当 immediate 为 false 的时候,因为使用了 setTimeout ,我们将 func.apply(context, args) 的返回值赋给变量,最后再 return 的时候,值将会一直是 undefined,所以我们只在 immediate 为 true 的时候返回函数的执行结果。
//再次修改function debounce(func, wait, immediate) { var timeout, result; return function () { var context = this; var args = arguments; if (timeout) clearTimeout(timeout); if (immediate) { // 如果已经执行过,不再执行 var callNow = !timeout; timeout = setTimeout(function(){ timeout = null; }, wait) if (callNow) result = func.apply(context, args) } else { timeout = setTimeout(function(){ func.apply(context, args) }, wait); } return result; }}//这个方法与上面的区别就是把返回值return出去。
PS: 以前在工作中遇到一些问题或者解决一些问题的时候,没把问题总结归纳,然后现在想开始把所有问题系统的归纳总结一下,从这篇开始会尽量每周写一篇,把知识点总结一下,不仅限于前端部分。
共同学习,写下你的评论
评论加载中...
作者其他优质文章