这里介绍一下函数防抖和函数节流,主要用js 举例。当你看完的时候你就发现本身之前就用过,只是不知道它的专业术语,好吧,让咱们来了解一下。闭包
什么是函数防抖呢?app
假设在这样一种状况下,好比说咱们这样那样但愿在滚动后,作某些操做,可是呢?函数
这里分析一下,就是要在滚动后,什么是滚动后呢?就是滚动不动了,那么就是滚动后。post
咱们能够监听滚动事件:this
//给页面绑定滑轮滚动事件 if (document.addEventListener) {//firefox document.addEventListener('onscroll', scrollFunc, false); } //滚动滑轮触发scrollFunc方法 //ie 谷歌 window.onmousewheel = document.onscroll= scrollFunc; function scrollFunc{ //方法 }
若是咱们滚动一下就去执行咱们的事件,那么就会形成不少事情,好比说如卡顿,再好比说执行屡次不符合咱们的预期,也就是功能没有实现。firefox
那么这个问题,就是由于咱们没有作到滚动后。有些系统没有提供滚动后的事件,那么咱们得本身实现。code
那么就得回到滚动后这个事件中来,滚动后就是滚动后一段时间内不动,那么就是滚动后。事件
那么能够这样写:ip
function debounce(fn,wait){ var timer = null; return function(){ if(timer !== null){ clearTimeout(timer); } timer = setTimeout(fn,wait); } } function handle(){ console.log("滚动结束"); } window.addEventListener("onscroll",debounce(handle,1000));
这里可能有你们困惑的一个问题,debounce 屡次执行,debounce 中的timer 没有执行不是会为空吗?那么(timer !== null) 不是不成立吗?get
这里就须要咱们看仔细了,咱们屡次执行的是:
function(){ if(timer !== null){ clearTimeout(timer); } timer = setTimeout(fn,wait); }
而不是debounce,由于闭包缘由,那么他们共享一个timer,因此是这样的了,一般共享一个timer 也是用闭包写法否则,全局的话会污染的。
那么这样就是结束了,或者说debounce 是否完善了? 答案是否认的,在咱们的handle 并不能获取到滚动的参数,好比说滚动的距离等,那么咱们须要传递一下。
还有一个缘由就是this的问题,若是不这样的话,this会变化的。
<div style="height: 200px;width: 200px;background-color: aqua;" id="test"></div> <script> function debounce(fn, wait) { var timer = null; return function () { let context = this; let args = arguments; if (timer !== null) { clearTimeout(timer); } timer = setTimeout(() => { fn.apply(context, args) }, wait); } } function handle() { console.log(this); } document.getElementById('test').onmousemove=debounce(handle, 1000); </script>
这个咱们获得的this是
<div style="height: 200px;width: 200px;background-color: aqua;" id="test"></div>。
若是不使用apply,那么是:Window {frames: Window, postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, …}
若是想详细了解这一块,须要去了解闭包这一块,能够去看个人闭包这一块,或者网上搜。
function debounce(fn, wait) { var timer = null; return function () { let context = this; let args = arguments; if (timer !== null) { clearTimeout(timer); } timer = setTimeout(()=>{fn.apply(context,args)}, wait); } }
这样就ok了。
在函数防抖中,还有另一种就是当即执行版。
当即执行的含义也是很是有意思的。仍是拿这个滚动说事。
我但愿在滚动的时候立马执行一个函数,可是呢,在以后继续滚动过程当中,但愿不要执行了。
function debounce(fn, wait) { var timer = null; return function () { let context = this; let args = arguments; let isCall = !timer; if (timer !== null) { clearTimeout(timer); } timer = setTimeout(() => { timer = null; }, wait); if(isCall){ fn.apply(context,args); } } }
实际上是这样一个过程,若是有timer,那么干好清理timer的事,若是没有timer 那么执行须要调用的函数。
为了咱们方便调用,能够结合成一个:
function debounce(fn, wait, immediate) { var timer = null; return function () { let context = this; let args = arguments; if (timer !== null) { clearTimeout(timer); } if (immediate) { let isCall = !timer; timer = setTimeout(() => { timer = null; }, wait); if (isCall) { fn.apply(context, args); } } else { timer = setTimeout(() => { fn.apply(context, args); }, wait); } } }
immediate 设置是当即执行版,仍是延迟版。
那么什么函数节流呢?假若有这样一个需求,有一个画板,如今咱们有一个需求就是在画画的时候,每隔几秒,保存一次当时的画板的状况。
那么这个时候不能单纯的settimerout,由于这里的需求是画画的时候,也就是咱们在画的时候。那么这个时候咱们要监听到手指移动事件,
而且几秒执行一次。
function throttle(fn,wait){ var timer = null; return function(){ let context = this; let args = arguments; if(!timer){ setTimeout(() => { timer=null; fn.apply(context,args); }, wait); } } }
每次timer=null的时候咱们才去设置settimeout ,这样就行了。
固然记时方式有不少,咱们也可使用时间戳的方式。
function throttle(fn,wait){ var previous=0; return function(){ let context = this; let args = arguments; var now=Date.now(); if(now-previous>wait){ fn.apply(context,args); previous=now; } } }
通常在项目中,两种通常取一个,而防抖通常都会用到,需求不同。