什么是防抖和节流
防抖:动作绑定事件,动作发生后在设置的一定时间后触发,如果在这个时间内,再次发生该动作,就要重新再等待一定时间再触发事件(事件隔一定时间时候还会触发)
节流:在动作绑定事件之后,动作发生后一段时间后触发事件,在这个时间内,如果再次触发这个动作,则会被无视,等到事件执行完毕再重新触发
为什么会用到防抖或者节流呢?
在实际中使用的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: 以前在工作中遇到一些问题或者解决一些问题的时候,没把问题总结归纳,然后现在想开始把所有问题系统的归纳总结一下,从这篇开始会尽量每周写一篇,把知识点总结一下,不仅限于前端部分。

随时随地看视频