函数的防抖
函数的防抖:就是对于一些 ==频繁== 的操作,在 ==一定的时间== 内只 ==执行一次== (第一次或最后一次)。 场景描述:比如页面中的按钮点击事件,一般情况下会给按钮绑定一个点击事件,然后当我们点击按钮时就会触发事件并执行某些功能代码(比如请求后台api)。正常情况我们都是点击一下触发一次,但是不排除有人恶意不停的点击按钮,这样的话就会不停的去请求后台api,势必会给服务器造成一定的压力,所以为了防止这种频繁的操作,函数的防抖就诞生了。 实现思路:利用闭包机制
- 需要预先设定一个等待时间(等待这段时间内仅执行一次代码) wait,设置定时器用于计时
- 当我们第一次点击按钮触发事件时开始计时(这里需要一个定时器),等待wait这么长时间
- 在这等待期间看是否有第二次事件触发,如果没有第二次触发,则等待时间结束后自动执行功能代码
- 如果这等待期间第二次事件又被触发,则清除原来的定时器,重新开始计时重新等待wait这么长时间,并且代码不会执行直到在这段时间内不再频繁触发,代码才会执行。
使用场景:一般用于按钮点击事件
函数语法: @params
- func[function]:要触发的函数,真正执行功能代码的函数
- wait[number]:等待时间
- immediate[boolean]:这里可以加个开关用于识别:是频繁操作的第一次执行还是最后一次执行
@return
- 可以被调用执行的函数
<button id="submit"></button>
//默认识别第一次,就是在该段时间内,只有第一次点击生效。
function debounce(func, wait = 300, immediate = true){
let timer = null;
return function anonymouse(...params){
//每次点击都要先清除定时器重新计时wait
clearTimeout(timer);
//只要timer不为null,则说明是频繁点击,是频繁点击就需要重新计算并且函数func只需执行一次
//一旦频繁点击结束,等待时间wait到了,则timer又会被重新置为null
let now = immediate && !timer;
timer = setTimeout(() => {
//等待时间到了以后需要重新将timer置为null,便于识别是否是下一轮频繁点击的第一次点击
timer = null;
//这里之所以用这种方式调用func,是不想将原函数(func)中的this指向改变
//当前这个匿名函数时由按钮触发,所以这里this指向的就是触发事件的那个按钮。
//而如果不用函数的防抖机制,直接把函数func绑定给按钮,则在func中的this指向也是触发事件的按钮。
//所以这里如果直接调用func函数的话,func中的this指向就变为window了。
//如果immediate为true则在第一次触发就已经执行,这里等待时间到就不再执行了
!immediate ? func.call(this, ...params) : null;
},wait);
//如果immediate为true说明是需要识别第一次触发,则调用函数执行
now ? func.call(this, ...params) : null;
}
}
function handle(){
console.log(this);
//调用API.....
}
submit.onclick = debounce(handle, 300, true);
函数的节流
函数的节流:节流就是在一段频繁的操作中,为了不让代码频繁的执行,预设一个时间,然后让代码每隔预设的这段时间执行一次。 场景描述:比如页面滚动事件,当我们在拖动滚动条时,对应的滚动条事件中的代码就会不停的执行,每一次滚动过程中,浏览器最快反应时间一般为5~6ms,而只要过了5 ~6ms,对应的函数就会被触发执行。所以 为了让代码执行的不那么频繁,我们会采用函数的节流来控制。 实现思路:利用闭包机制
- 需要预先设定一个等待时间(每隔多长时间可以执行一次代码) wait,设置定时器timer用于计时
- 当滚动条刚开始滚动第一次执行时,记下当前时间now,并将上一次执行时间previous设置为0,这样能保证刚开始滚动时肯定能执行一次
- 用间隔时间wait减去当前时间now再减去上次执行时间previous,就是离我预设的时间还剩余多长时间remaining
- 如果剩余时间remaining小于等于0,则说明两次函数执行的时间间隔已经过了我们预设的间隔时间,则可以进行下一次执行 调用具体执行函数func,同时将当前时间now赋值给previous作为上一次执行时间
- 如果剩余时间remaining大于0 并且定时器为空(没设置或手动清空过),则说明间隔时间还没到,并且也没有设置计时器倒计时;这时就需要设置一个定时器开始倒计时,倒计时的时间就是剩余时间remaining,如果到计时结束了则可以再次执行代码,同时将定时器timer清空,便于下次设置定时器;然后将当前执行时间赋值给previous作为上一次执行时间待用。
使用场景:一般用于页面滚动、文本框输入过程中的模糊匹配
函数语法: @params
- func[function]:要触发的函数,真正执行功能代码的函数
- wait[number]:间隔时间
@return
- 可以被调用执行的函数
function throttole(func, wait){
let timer = null,
previous = 0;
return function anonmouse(...params){
let now = new Date(),
remaining = wait - (now - previous);
if(remaining <= 0){//两次执行时间已经超过设置的间隔时间了,可以执行了
//将当前执行时间作为上次执行时间赋值给previous
previous = now;
//取消倒计时,因为这里已经执行了
clearTimeout(timer);
//执行完成后清空timer,便于下次设置倒计时定时器
timer = null;
func.call(this, ...params);
}else if(!timer){
//两次执行时间差还不到预设的间隔时间,并且也没有设置定时器倒计时;
//则设置定时器开始倒计时,时间结束就又可以执行了
//如果已经设置了定时器,就不再做任何处理了。
timer = setTimeout(() => {
//执行完成后清空timer,便于下次设置倒计时定时器
timer = null;
//将当前执行时间作为上次执行时间赋值给previous
previous = new Date();
func.call(this, ...params);
}, remaining);
}
}
}
function handle(){
console.log(this);
//dosomthing.....
}
document.body.onscroll = throttole(handle, 500);