防抖
我们经常在业务中判断当前用户名是否存在,如代码1所示,如果每次触发input的change事件都向后台请求一次接口,那就非常没有效率,因此我们可以判断用户输入结束后再发送数据。
//代码1
<input type="text" name="userName">
<script>
document.querySelector('input').addEventListener('input',function(){
console.log('我向后台验证了一次用户名')
//这样每次用户输入值的时候都会请求一次接口,效率低下
})
</script>
如何判断用户是否结束输入呢?如果input的input事件(keydown事件)n秒内没有被触发,则我们是否可以认为输入已经结束?
//代码2
//防抖1.0 实现了基本的防抖,但仍然具有问题
//如首次输入后不会立即执行函数,代码3解决了改问题
document.querySelector('input').addEventListener('input',function(){
if(timer) clearTimeout(timer);
var timer=setTimeout(function(){
console.log('我向后台验证了一次用户名')
},1000)
})
//代码3
//防抖2.0 先立即执行一次函数,然后再防抖
document.querySelector('input').addEventListener('input',function(){
if(timer) clearTimeout(timer);
let im= !timer;
var timer=setTimeout(function(){
console.log('我向后台验证了一次用户名')
},1000)
if(im) {console.log('我向后台验证了一次用户名')}
})
每次防抖都要写一遍是不是感觉很烦,接下来我们一起封装一下防抖吧
//代码4
//防抖3.0 对防抖进行简单的封装
function noShake(callback,time){
let timer;
return function(){
clearTimeout(timer);
timer=setTimeout(function(){
callback(); //执行callback();
},time)
}
}
document.querySelector('input').addEventListener('input',noShake(function(){
console.log('我向后台验证了一次用户名')
console.log(this.value); //会是input的值吗
},500))
如果忽略没有自动执行的问题,代码4是不是完美的呢?
显然不是,我们忽略了this,还有函数中参数的问题。接下来我们来解决它
//代码5
//防抖4.0
function noShake(callback,time){
let timer;
return function(){
let s=this,args=arguments; //获取this,和arguments
clearTimeout(timer);
timer=setTimeout(function(){
callback.apply(s,args); //apply改变this的指向
},time)
}
}
document.querySelector('input').addEventListener('input',noShake(function(event){
console.log(this.value);
console.log(event); //完美解决
},500))
代码5的核心理念就是apply改变this指向,(不清楚apply等方法的同学可以点此跳转)
接下来我简单分析一下代码5,首先回到代码2
//代码6
//代码2 最初的防抖
document.querySelector('input').addEventListener('input',function(){
if(timer) clearTimeout(timer);
timer=setTimeout(function(){
console.log('我向后台验证了一次用户名')
},1000)
})
//我们是不是可以把代码2结合代码5改装成下面的形式
var timer;
document.querySelector('input').addEventListener('input',function(){
function callback(){
console.log(this.value) //很明显,当前this指向window
}
let s=this,args=arguments; //当前this指向input
if(timer) clearTimeout(timer);
timer=setTimeout(function(){
callback.apply(s,args); //改变this指向
},1000)
})
//是不是清晰很多
//代码5中return function(){}作用域的this指向input,而内部的定时器是默认指向window的,所以需要改变this指向。
节流
和防抖差不多,节流是无论用户输入停不停,每隔n秒就执行一次,直接贴代码
//代码7
//节流
function save(callback,time){
let start = 0;
return function(){
let now = Date.now(); //获取当前时间
if (now - time > start){ //时间是否已经过去time豪秒。
callback.apply(this,arguments);
start = now; //重置start
}
}
}
document.querySelector('input').addEventListener('input',save(function(){
console.log(this.value)
},500))
//也可以把时间判断换成boolean变量
function save(callback,time){
let flag=true;
return function(){
if(!flag) return;
flag=false;
callback.apply(this,arguments);
setTimeout(function(){
flag=true;
},time)
}
}