前言
昨天晚上考核开了个会,被师兄师姐问到为啥搜索不写防抖节流,所以今天就给它补上了。
一、防抖和节流是什么?
- 防抖:就我个人的粗浅理解,防抖就是如果连续多次在规定间隔时间触发同一事件,那么只会执行一次。(它还分有立即执行版本和非立即执行版本)
- 节流:还是就我个人的粗浅理解,节流就是如果连续多次触发同一事件,那么在你连续触发的时候该事件会以你设定的间隔时间被触发(它有时间戳版本和定时器版本)
- 我觉得我不能准确用文字描述出来,所以只能尽量在下面配合代码看看能不能说清楚点了。
二、防抖
1.非立即执行版本
代码如下(示例):
-
function debounce(func,wait) { let timeout; return function() { let context = this; let args = arguments; clearTimeout(timeout); timeout = setTimeout(function() { func.apply(context,args) },wait) } }
- 下面是粗浅的解释:
- 非立即执行就是在连续触发中不会执行,直到最后一次触发等待wait时间后才会执行函数
- 进入函数首先声明一个timeout(为下方的定时器)
- 此次是高阶函数,返回了一个函数function,在function里面用context接收this,用args接收arguments(因为不知道函数参数的个数,所以需要这个)
- 如果不用context接收this,直接在下面定时器func.apply(this,args)的话,那么这个this指向的是window,而不是func的this
- 每次触发进入function,就清空一次定时器,让其无法执行,直到最后一次触发,等待wait时间后,才会执行func.apply(context,args)调用传入的函数
2.立即执行版本
代码如下(示例):
function debounce(func,wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
// 第一次进入时,timeout为undefined,!timeout为真,直接触发
if(!timeout) {
func.apply(context,args)
}
// 之后进入时,如果timeout存在,就把它设为null,让他无法执行func
if(timeout) {
clearTimeout(timeout)
}
// 每次进入重新设置timeout,刷新wait,直到最后一次等待wait后执行func
timeout = setTimeout(() => {
func.apply(context,args)
},wait)
}
}
- 下方是粗浅的解释:
- 立即执行版本就是在触发的时候会立即执行一次函数,然后在最后等待wait时间后才会再次触发
- 这里的context和args同上
- 第一次进入时,由于timeout是undefined,所以!timeout为真,立即执行了一次函数
- 之后进入时,如果timeout存在,就把它设为null,让他无法执行func
- 每次进入重新设置timeout,刷新wait,直到最后一次等待wait后执行func
3.二合一版本
function debounce(func,wait,immediate) {
let timeout;
return function() {
let context = this;
let args = arguments;
clearTimeout(timeout);
// 立即执行
if(immediate) {
// 第一次进入的时候tiomeout为undefined,此时callNow为真
let callNow = !timeout;
//如果在wait内再次触发,则timeout还没等于null,也就是callNow为假,不会执行函数
//直到某次超过wait了,那么timeout等于null,callNow为真,最后会执行一次
timeout = setTimeout(() => {
timeout = null;
},wait)
if(callNow) { //第一次进入的时候callNow为真,立即执行一次
func.apply(context,args)
}
} else {
// 不会立即执行
timeout = setTimeout(function() {
func.apply(context,args)
},wait)
}
}
}
- 下方是粗浅的解释:
- 啊解释都在注释里了我就不再复制一遍了吧
- 主要区别就是多了一个immediate,当它为true时,会立即执行,不为true时,不会立即执行
- 使用方法:
debounce(func,200,true)
三、节流
1.时间戳版本
function throttle(func,wait) {
let context,args;
// 之前的时间戳
let previous = 0;
return function() {
context = this;
args = arguments;
// 获取当前的时间戳
let now = new Date().valueOf()
if(now - previous > wait) { // 因为第一次的now很大,所以会立即触发
// 立即执行
func.apply(context,args)
previous = now;
}
}
}
- 在首次进入时,声明一个前时间戳previous为0
- 在return 的function里面,获取当前的时间戳now
- 因为第一次获取的now很大,所以第一次的now-previous一定大于wait(除非你的wait设置了很多年),会立即执行func,在执行完毕后让previous=now(把执行时刻的时间戳赋给前时间戳)
- 在后方不断触发的时候会不断重新获取现有时间戳,以wait为间隔执行func
2.定时器版本
function _throttle(func,wait) {
let context,args,timeout;
return function() {
context = this;
args = arguments;
if (!timeout) {
timeout = setTimeout(() => { // 当第一次进入的时候,会在wait到点后触发,然后timeout为null,能再次进入,如果没到wait时间,timeout有值,就进不去
func.apply(context,args)
timeout = null;
},wait)
}
}
}
- 在第一次进入的时候,timeout为undefined。!timeout为真,进入if为timeout设置执行内容
- 如果在还没到wait时间内再次触发,此时timeout是有值的,不会进入if,也就不会重置timeout
- 等到wait后执行timeout内的语句,执行了func,并把timeout设为null,使执行后再次触发可以重新给timeout设置执行内容
总结
- 本人水平仍不足,解释可能解释得很乱,不能把想要表达的东西清晰地表达出来