- throttle(节流): 设置一个阈值,在阈值内,把触发的事件合并成一次执行,当达到阈值,必行执行
- debounce(去抖动): 把触发非常频繁的事件合并成一次执行
throttle
方法一:时间戳
利用本次执行的时间与上次执行的时间相比较
特点:先执行目标函数,后等待规定的时间段, 一般首次都会触发, 最后一次如果时间不满,不会触发
1 2 3 4 5 6 7 8 9 10
| function throttle(fn, delay) { let startTime = +new Date; return function() { const curTime = +new Date(); if (curTime - startTime >= delay) { fn.apply(this.arguments); startTime = curTime; } }; }
|
方法二:定时器
利用定时器实现时间间隔
特点:先等待够规定时间,再执行,即停止触发后,若定时器已经在任务队列里注册了目标函数,它也会执行最后一次。首次不会触发
1 2 3 4 5 6 7 8 9 10 11 12 13
| function throttle(fn, wait) { var time, context; return function() { context = this; if(!time) { time = setTimeout(function() { fn.apply(context, arguments); time = null; }, wait); } } }
|
方法三:时间戳+定时器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| function throttle(func, wait) { let previous = 0; let context, args, time, remaining; return function() { let now = +new Date(); context = this; args = arguments; remaining = wait - (now - previous); if (remaining <= 0) { func.apply(context, args); previous = now; } else { if (time) clearTimeout(time); time = setTimeout(function() { func.apply(context, args); time = null; previous = +new Date(); }, remaining); } } };
|
方法四:underscore的实现方法
通过leading, trailing对首尾是否调用
leading:false 表示禁用第一次执行
trailing: false 表示禁用停止触发的回调
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| const throttle = function(func, wait, options) { var timeout, context, args, result; var previous = 0; if (!options) options = {}; var later = function() { previous = options.leading === false ? 0 : +new Date; timeout = null; result = func.apply(context, args); if (!timeout) context = args = null; }; var throttled = function() { var now = +new Date(); if (!previous && options.leading === false) previous = now; var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0 || remaining > wait) { if (timeout) { clearTimeout(timeout); timeout = null; } previous = now; result = func.apply(context, args); if (!timeout) context = args = null; } else if (!timeout && options.trailing !== false) { timeout = setTimeout(later, remaining); } return result; }; throttled.cancel = function() { clearTimeout(timeout); previous = 0; timeout = context = args = null; }; return throttled; };
|
适用场景
频繁触发时间,且在一定时间内需要执行
- 鼠标移动,
- 窗口滚动
debounce
###代码
1 2 3 4 5 6 7 8 9 10 11
| function debounce(func, wait) { var timeout; return function() { let context = this, args = arguments; clearTimeout(timeout); timeout = setTimeout(function() { func.apply(context, args); }, wait); }; }
|
有时候希望能实现 在一个周期内第一次触发,就立即执行一次,然后一定时间段内都不能再执行目标函数
优化后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| var debounce = function(func, wait, immediate) { var timeout, args, context, timestamp, result; var later = function() { var last = +new Date() - timestamp; if (last < wait && last >= 0) { timeout = setTimeout(later, wait - last); } else { timeout = null; if (!immediate) { result = func.apply(context, args); if (!timeout) context = args = null; } } }; return function() { context = this; args = arguments; timestamp = +new Date(); var callNow = immediate && !timeout; if (!timeout) timeout = setTimeout(later, wait); if (callNow) { result = func.apply(context, args); context = args = null; } return result; }; };
|
如果immediate为true的话,立刻调用,然后wait内不可以再次调用,
可以用于提交等操作
适用场景
频繁触发事件, 且可以合并处理
- 用户输入搜索
- 浏览器resize