在实际开发中,咱们常常须要绑定一些持续触发的事件,如resize、scroll,mousemove,input等,浏览器在默认状况下会对事件处理函数无限制的调用,这样就会加剧浏览器的负载,致使用户体验不好,有些还会频繁向后台请求数据,对服务器形成没必要要的压力有些状况下咱们不须要事件持续触发过程当中频繁的去执行事件处理函数,或者更但愿把屡次计算合并成一次,只操做一个精确点。对此,咱们能够采用防抖(debounce)和节流(throttle)的方式来减小调用频率,同时又不影响实际效果。
ajax
函数防抖(debounce)浏览器
函数防抖(debounce):当持续触发事件时,必定时间段内没有再触发事件,事件函数就执行一次,若是在该时间段内又触发了事件,就从新开始延时。也就是说当一个用户一直触发这个事件函数,且每次触发函数的间隔小于设定事件,那么防抖的状况下就执行一次。服务器
如上图,在持续触发scroll事件时,并不执行handle函数,当1000毫秒内没有触发scroll事件时,才会延时触发scroll事件app
防抖函数使用场景:dom
实现一个简单的debounce:函数
1 // 防抖 2 function debounce(fn, wait) { 3 var timeout = null; 4 return function () { 5 if (timeout !== null) clearTimeout(timeout); 6 timeout = setTimeout(fn, wait); 7 } 8 } 9 // 处理函数 10 function handle() { 11 console.log(Math.random()); 12 } 13 // 滚动事件 14 window.addEventListener('scroll', debounce(handle, 1000));
当持续触发scroll事件时,事件处理函数handle只在中止滚动1000毫秒以后才会调用一次,也就是说在持续触发scroll事件的过程当中,事件处理函数handle一直没有执行。网站
函数节流(throttle)this
函数节流(throttle):当持续触发事件时,保证必定时间段内只调用一次事件处理函数。节流通俗解释就好比咱们水龙头放水,阀门一打开,水哗哗的往下流,秉着勤俭节约的优良传统美德,咱们要把水龙头关小点,最好是如咱们心意按照必定规律在某个时间间隔内一滴一滴的往下滴。spa
如上图,持续触发scroll事件时,并不当即执行handle函数,每隔1000毫秒才会执行一次handle函数。code
防抖函数使用场景:
窗口调整(resize)
页面滚动(scroll)
DOM元素的拖拽功能实现(mousemove)
1 var throttle = function (func,delay){ 2 var prev = Date.now(); 3 return function (){ 4 var context = this; 5 var args = arguments; 6 var now = Date.now(); 7 if(now - prev >= delay){ 8 func.apply(context,args); 9 prev = Date.now(); 10 } 11 } 12 } 13 // 事件处理函数 14 function handle(){ 15 console.log(Math.random()); 16 } 17 // scroll事件 18 window.addEventListener('scroll',throttle(handle,1000));
当高频事件触发时,第一次会当即执行(给scroll事件绑定函数与真正触发事件的间隔通常大于delay),然后再怎么频繁地触发事件,也都是每delay时间才执行一次。而当最后一次事件触发完毕后,事件也不会再被执行了 (最后一次触发事件与倒数第二次触发事件的间隔小于delay,为何小于呢?由于大于就不叫高频了呀)。
另外一个throttle例子(定时器版):
1 var throttle = function (func, delay) { 2 var timer = null; 3 return function () { 4 var context = this; 5 var args = arguments; 6 if (!timer) { 7 timer = setTimeout(function () { 8 func.apply(context, args); 9 timer = null; 10 }, delay); 11 } 12 } 13 } 14 15 function handle() { 16 console.log(Math.random()); 17 } 18 window.addEventListener('scroll', throttle(handle, 1000));
当触发事件的时候,咱们设置一个定时器,再次触发事件的时候,若是定时器存在,就不执行,直到delay时间后,定时器执行执行函数,而且清空定时器,这样就能够设置下个定时器。当第一次触发事件时,不会当即执行函数,而是在delay秒后才执行。然后再怎么频繁触发事件,也都是每delay时间才执行一次。当最后一次中止触发后,因为定时器的delay延迟,可能还会执行一次函数。
节流中用时间戳或定时器都是能够的。更好的是结合时间戳与定时器使用,当第一次触发事件时立刻执行事件处理函数,最后一次触发事件后也还会执行一次事件处理函数。
另外一个throttle例子(时间戳+定时器版):
1 var throttle = function (func, delay) { 2 var timer = null; 3 var startTime = Date.now(); 4 return function () { 5 var curTime = Date.now(); 6 var remaining = delay - (curTime - startTime); 7 var context = this; 8 var args = arguments; 9 clearTimeout(timer); 10 if (remaining <= 0) { 11 func.apply(context, args); 12 startTime = Date.now(); 13 } else { 14 timer = setTimeout(func, remaining); 15 } 16 } 17 } 18 19 function handle() { 20 console.log(Math.random()); 21 } 22 window.addEventListener('scroll', throttle(handle, 1000));
在节流函数内部使用开始时间startTime、当前时间curTime与delay来计算剩余时间remaining,当remaining<=0时表示该执行事件处理函数了(保证了第一次触发事件就能当即执行事件处理函数和每隔delay时间执行一次事件处理函数)。若是还没到时间的话就设定在remaining时间后再触发 (保证了最后一次触发事件后还能再执行一次事件处理函数)。固然在remaining这段时间中若是又一次触发事件,那么会取消当前的计时器,并从新计算一个remaining来判断当前状态。
总结:
函数防抖:将几回操做合并为一此操做进行。原理是维护一个计时器,规定在delay时间后触发函数,可是在delay时间内再次触发的话,就会取消以前的计时器而从新设置。这样一来,只有最后一次操做能被触发。
函数节流:使得必定时间内只触发一次函数。原理是经过判断是否到达必定时间来触发函数。
区别: 函数节流无论事件触发有多频繁,都会保证在规定时间内必定会执行一次真正的事件处理函数,而函数防抖只是在最后一次事件后才触发一次函数。 好比在页面的无限加载场景下,咱们须要用户在滚动页面时,每隔一段时间发一次 Ajax 请求,而不是在用户停下滚动页面操做时才去请求数据(常见的大型网站在进行数据访问时都是经过滚动页面每隔一段时间发送一个ajax进行数据请求)。这样的场景,就适用函数节流来实现。