原生js实现函数防抖和节流
防抖和节流的本质都是限制函数的执行次数
防抖:前面的所有的触发都被取消,最后一次执行在规定的的时间之后才会触发,也就是说如果连续快速的触发,只会执行一次
通过setTimeout的方式,在一定时间间隔内,将多次触发的变成一次触发
<button>提交</button> <script> let button=document.querySelector("button") button.addEventListener('click',debounce(fun),false) //触发函数 function fun(){ console.log(123) } //防抖函数 function debounce(fun){ let time=null return function(){ if(time){ clearTimeout(time) } time=setTimeout(function(){ fun() }, 1000); } } </script>
此时就可以实现效果,
但是此时会存在两个问题:
当前的事件对象如何获取,
this指向问题
在没有用防抖函数包装之前
button.addEventListener('click',fun,false) //触发函数 function fun(e){ console.log(this) console.log(e) }
可以看到this的指向
但是当被防抖函数包装之后
<script> let button=document.querySelector("button") button.addEventListener('click',debounce(fun),false) //触发函数 function fun(e){ console.log(this,"fun") console.log(e,"fun") } //防抖函数 function debounce(fun){ let time=null return function(e){ console.log(this,"debounce") console.log(e,"debounce") if(time){ clearTimeout(time) } time=setTimeout(function(){ fun() }, 1000); } } </script>
如果我们想要调用fun函数的参数
通过arguments来获取
function debounce(fun){ let time=null return function(){ console.log(arguments) if(time){ clearTimeout(time) } time=setTimeout(function(){ fun() }, 1000); } }
也就是说,此时输出的第0项,就是事件对象
所以此时这样写
<script> let button=document.querySelector("button") button.addEventListener('click',debounce(fun),false) //触发函数 function fun(e){ console.log(this,"fun") console.log(e,"fun") } //防抖函数 function debounce(fun){ let time=null return function(){ if(time){ clearTimeout(time) } time=setTimeout(()=>{ // 函数里面不存在arguments对象,所以此时使用箭头函数,用的外层的arguments //通过apply方式绑定当前this指向 fun.apply(null,arguments) }, 1000); } } </script>
就可以拿到arguments参数问题
我们在看this指向的问题
<script> let button=document.querySelector("button") button.addEventListener('click',debounce(fun),false) //触发函数 function fun(e){ console.log(this,"fun") console.log(e,"fun") } //防抖函数 function debounce(fun){ let time=null return function(){ console.log(this,"debounce") if(time){ clearTimeout(time) } time=setTimeout(()=>{ // 函数里面不存在arguments对象,所以此时使用箭头函数,用的外层的arguments //通过apply方式绑定当前this指向 fun.apply(null,arguments) }, 1000); } } </script>
此时输出的this指向
因为我们这里apply绑定了this,所以fun中的this是为空,指向window的
所以此时我们将debounce函数中this通过参数的形式来指向箭头函数外部的this对象
fun.apply(this,arguments)
此时就可以拿到了,
如果此时我们第一次不想走这个延时
也就是说,第一次点击的时候就立即响应,第二次在进行防抖
<script> let button = document.querySelector("button"); button.addEventListener("click", debounce(fun, 1000, true), false); //触发函数 function fun(e) { console.log(this, "fun"); console.log(e, "fun"); } //防抖函数 //第一次点击 function debounce(fun, timer, trag) { let time = null; return function () { if (time) { clearTimeout(time); } if (trag) { //设置判断,如果是第一次就立即执行,将time转换成true var first = !time; if (first) { fun.apply(this, arguments); } time = setTimeout(() => { // 第一次立即执行过去之后将time恢复原样,然后走防抖 time=null }, timer); } else { time = setTimeout(() => { // 函数里面不存在arguments对象,所以此时使用箭头函数,用的外层的arguments //通过apply方式绑定当前this指向 fun.apply(this, arguments); }, timer); } }; } </script>
节流:在规定的间隔事件范围内不会重复触发回调,只有大于这个事件间隔才会触发回调,把频繁触发变为少量触发
<button>提交</button> <script> let button = document.querySelector("button"); button.addEventListener("click", debounce(fun, 1000), false); //触发函数 function fun() { console.log(123) } //防抖函数 //第一次点击 function debounce(fun, delay) { var begin=0; return function(){ var time=new Date().getTime() //如果当前的时间减去开始的时间大于设置的延时delay的时候再去执行 if(time-begin>delay){ fun.apply(this,arguments) begin=time } } } </script>