一、起因:
最近在学习防抖的一些知识,然后就和好兄弟交流,然后好兄弟说:“这种知识点要用到再好好学会记得更深刻。”,然后我就想我学以致用也挺不错,刚好这时候好兄弟和我(我比较菜,好兄弟带着我写的)在写学校的一个项目,里面用到了模糊查询,就想着能不能活学活用一把,然后就开始了折腾。
二、可行性分析:
然后就开始分析在模糊查询里使用防抖函数好处在哪呢?因为这个模糊查询是绑定了@input事件的,也就是每次在文本框内的input的都会触发查询事件,都会发送请求向后台拿查询之后的数据,但是如果你输入是一个字一个字输入的话,那么就会有很多不必要的请求,因为每次输入一个字都会请求查询一次,且每删除一个字也都会请求一次,所以在input事件这里加个防抖函数还是很有用的!
三、说干就干(疯狂折腾):
1.当时想的应该是很简单,直接@input=“debounce(func,delay)”就可以了,这里的func就是我们的模糊查询函数,在原生js中防抖函数不是要return function(){},然后返回的函数里面再写防抖的逻辑吗?(因为如果不把逻辑写在返回的函数里面,没有触发事件就会立刻执行,但是vue中不会!,vue要触发事件才会执行对应函数!我当时对vue的了解也不多,然后就当作它会去做了!所以才导致了之后的折腾!)然后当我直接@input=“debounce(func,delay)”时,发现程序根本进不去return function(){}里面去?这是为什么呢?然后好兄弟发现应该是vue的原因,无论是@input=“debounce”还是@input=“debounce(func,delay)”都是只触发debounce这个函数,而不触发这个函数的返回值!所以每次input只执行debounce这个函数,但是不执行他的返回值,也就是不执行他返回的函数!所以我们现在将@input=“debounce(func,delay)”改为@input=“debounce(func,delay)()”就能解决第一个问题啦! 然后我们就开始写防抖函数的逻辑啦。一开始代码如下:
function debounce(func,delayTime){
//记录时间
let timer;
//记录搜索次数
let count = 0
//在函数里面返回函数,不然会立即执行func(),不能做到input之后才执行func()
return function(){
//clearTimeout要放在延时执行之前,用来每次点击都清除延时
clearTimeout(timer);
//第一次直接搜索,后面的才开始防抖,更符合场景
if(count==0)
{
func();
count++;
}
else{
timer = setTimeout( function(){
func();
count++;
},delayTime);
}
}
}
2.然后就又来坑了。发现每次搜索次数都是0,然后都是走第一种情况,导致第二种情况的防抖完全没作用。。。然后就开始调试找错误到底在哪里,发现这个count每次进入return function(){ }之后都变为了0,我就疑惑了,这难道不是闭包吗?怎么每次count++都没用呢?结果好兄弟又看出来了,因为我前面@input是绑定的@input=“debounce(func,delay)()”,所以每次不只是执行返回的函数,同时也执行了ebounce(func,delayTime),所以每次都执行了let count=0,所以count一直都是0,第一点我们用@input=“debounce(func,delay)()”解决了我们input绑定不了debounce(func,delay)函数返回值的问题,却又造成了新的问题:每次都会重新执行整个函数,导致闭包失效了!这里再讲细一点:其实就是a()()和var x = a();x()的不同!a()()是函数a和函数的返回函数都执行!x = a();x()是只执行a函数的返回函数! 所以找到问题之后就好解决了,闭包不就是为了有两个个公共变量(timer,count)吗?那我们不直接在vue的data里面return两个变量就ok,然后就直接在data里面申明了timer,count变量(count变量赋值为0)之后就写了如下代码.
function debounce(func,delayTime){
return function(){
clearTimeout(this.timer);
if(this.count==0)
{
console.log('第一种情况')
func();
this.count++;
}
else{
this.timer = setTimeout(function(){
func();
this.count++;
},delayTime)
}
}
}
3.然后直接报错!说conut变量undefined,我心里???我这不是在data里面申明了嘛?这凭什么count没定义啊?然后仔细一想,发现事情不对,count是定义了,但是这个this好像不是一般的this,作用域好像不同咧。错也只能这里出错了,然后马上知错就改,就用that把外面的作用域直接弄进来!代码又变成如下:
function debounce(func,delayTime){
let that = this;
return function(){
clearTimeout(that.timer);
if(that.count==0)
{
console.log('第一种情况')
func();
that.count++;
}
else{
that.timer = setTimeout(function(){
func();
that.count++;
},delayTime)
}
}
}
4.到这里其实已经成功了,但是好兄弟说还可以优化,不用that,可以直接绑定整个函数,然后叫我用bind绑定一下会更方便,然后我就又用bind试了下,代码如下:(这里为什么不能用apply和call?因为apply和call返回的是绑定上下文this之后的函数执行的结果,bind返回的就是绑定上下文this之后的函数,所以这里要求返回的就是函数,而不是要结果!)
function debounce(func,delayTime){
return function(){
console.log(this.count,'搜索次数');
clearTimeout(this.timer);
if(this.count==0)
{
func();
this.count++;
}
else{
this.timer = setTimeout(function(){
func();
this.count++;
},delayTime)
}
}.bind(this)
}
5.看起来很有道理是吧,然后又出问题啦!count一直是1,不会随着搜索次数增加而增加了!那肯定是
else{
this.timer = setTimeout(function(){
func();
this.count++;
},delayTime)
}
这一段的this.count++出了问题,结果发现setTimeout里面的this和外面的this又不同,所以需要将这里的this和外面的this同步就ok,所以这里又运用到了箭头函数!箭头函数不就是指向在定义的时候继承自外层第一个普通函数的this,这样一来这个setTimout里面的this就和外面一样了,终于!这个问题得到了完美完美的解决!最终代码如下:
function debounce(func,delayTime){
return function(){
clearTimeout(this.timer);
if(this.count==0)
{
console.log('第一种情况')
func();
this.count++;
}
else{
this.timer = setTimeout(() =>{
func();
this.count++;
},delayTime)
}
}.bind(this)
}
四、总结:
没有想到一个简单的想法和实践,竟然会牵扯出这么多的东西,只能说自己还是太菜了,不过这次折腾还是让我学到了很多很多的东西,也感谢好兄弟的指点,也希望各位也能从中学到东西!如果都看到这里了,就点个赞再走吧哈哈哈哈!