防抖与节流小案例
在上一篇文章中提到了防抖与节流函数在js中的实际应用,这个直接和用户体验以及浏览器加载,服务器负载相关,所以防抖与节流对于前端开发人员来说是必须要掌握的一种技术。下面就根据一个小案例来具体体现防抖与节流的作用。
首先,没有进行防抖与节流处理的鼠标移动事件:
1 var num = 1; 2 var oWrap = document.getElementById('wrap'); 3 function count (){ 4 oWrap.innerHTML = num++; 5 } 6 oWrap.onmousemove = count;
上面代码中,给div绑定了鼠标移动事件,当鼠标在盒子内频繁移动时,事件处理函数也在持续的被调用,这样会对浏览器造成不必要的负载。
防抖函数(debounce)
防抖函数(debounce):指事件在触发后指定时间内只执行一次,若在指定时间内又触发了事件,则重新计算函数执行时间,防抖函数分为立即执行版和非立即执行版
非立即执行版:
1 function debounce(fn,wait){ 2 var timeout; 3 return function(){ 4 var context = this; 5 var args = arguments; 6 if(timeout){ 7 clearTimeout(timeout); 8 } 9 timeout = setTimeout(()=>{ 10 fn.apply(context,args) 11 },wait); 12 } 13 } 14 oWrap.onmousemove = debounce(count,1000);
上述非立即执行防抖函数结果显示为鼠标在盒子内频繁移动(此处gif看不出效果,不过真实在移动)时,函数不会立即执行,在1000ms过后会执行一次事件函数,当我在触发事件后在1s内再次触发事件会重新计算执行时间并在结束事件后执行函数
立即执行版:
1 function debounce(fn,wait){ 2 var timeout; 3 return function(){ 4 var context = this; 5 var args = arguments; 6 if(timeout){ 7 clearTimeout(timeout); 8 } 9 var callNow = !timeout; 10 timeout = setTimeout(()=>{ 11 timeout = null; 12 },wait) 13 if(callNow){ 14 fn.apply(context,args); 15 } 16 } 17 } 18 oWrap.onmousemove = debounce(count,1000);
立即执行版防抖函数是在触发事件后立即执行,并在1000ms后事件再次触发时调用执行函数,若在延迟时间内再次触发会重新计算执行时间
结合版:
1 function debounce(fn,wait,immediate){ 2 var timeout; 3 return function (){ 4 var context = this; 5 var args = arguments; 6 if(timeout){ 7 clearTimeout(timeout); 8 } 9 if(immediate){ 10 // immediate判断函数为立即执行还是非立即执行(true为立即执行,false为非立即执行) 11 var callNow = !timeout; 12 timeout = setTimeout(()=>{ 13 timeout = null; 14 },wait) 15 if(callNow){ 16 fn.apply(context,args); 17 } 18 else{ 19 timeout = setTimeout(function(){ 20 fn.apply(context,args); 21 },wait); 22 } 23 } 24 } 25 } 26 oWrap.onmousemove = debounce(count,1000,1)
二者结合版是在事件触发后会立即执行一次函数并在1000ms后再次执行一次函数(注:存在bug,哈哈!!!当触发事件后在延迟时间内再次触发或者频繁触发事件依旧会执行多次函数)
节流函数(throttle)
节流函数(throttle):指事件在频繁触发后在指定时间内触发一次,节流会稀释函数的执行频率,对于节流,存在时间戳和定时器两种方式
时间戳版:
1 function throttle(func, wait) { 2 var previous = 0; 3 return function () { 4 var now = Date.now(); 5 var context = this; 6 var args = arguments; 7 if (now - previous > wait) { 8 func.apply(context, args) 9 previous = now; 10 } 11 } 12 } 13 oWrap.onmousemove = throttle(count,1000)
可以看出,鼠标在盒子内频繁移动时,函数会立即执行,且每1s执行一次
定时器版:
1 function throttle(fn,wait){ 2 var timeout; 3 return function(){ 4 var context = this; 5 var args = arguments; 6 if(!timeout){ 7 timeout = setTimeout(()=>{ 8 timeout = null; 9 fn.apply(context,args) 10 },wait) 11 } 12 } 13 } 14 oWrap.onmousemove = throttle(count,1000)
定时器版节流函数在事件触发后不会立即执行,会在1s后执行一次,当持续触发时会每隔1s执行一次,在停止触发事件后,函数会再执行一次
结合版:
1 function throttle(fn, wait, type) {
// type判断节流函数方式,1为时间戳版,2为定时器版 2 if (type === 1) { 3 var previous = 0; 4 } else if (type === 2) { 5 var timeout; 6 } 7 return function () { 8 var context = this; 9 var args = arguments; 10 if (type === 1) { 11 var now = Date.now(); 12 13 if (now - previous > wait) { 14 fn.apply(context, args); 15 previous = now; 16 } 17 } else if (type === 2) { 18 if (!timeout) { 19 timeout = setTimeout(() => { 20 timeout = null; 21 fn.apply(context, args) 22 }, wait) 23 } 24 } 25 } 26 } 27 oWrap.onmousemove = throttle(count,1000,2)
参考文章:
https://github.com/mqyqingfeng/Blog/issues/22
https://github.com/mqyqingfeng/Blog/issues/26