节流(throttle)与防抖(debounce)
运行案例
运行代码
<div id="content"
style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;">
</div>
<script>
let num = 1;
const content = document.getElementById('content');
function count() {
content.innerHTML = num++;
};
content.onmousemove = count;
</script>
运行效果
当鼠标在 div(灰色)区域中移动的时候会持续地去触发该事件导致频繁执行函数。
防抖(debounce)
所谓防抖,就是指触发事件后 n 秒后才执行函数,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
防抖函数分为非立即执行版和立即执行版。
防抖(debounce)非立即执行版:
非立即执行版触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
/**
* @description:
* @param {Function} func 事件函数
* @param {Number} wait 事件设置的延迟事件 (1000 = 1 秒)
* @return (Function): 返回新的 debounced(防抖动)函数。
*
*/
function debounce(func, wait) {
let timeout;
return function () {
/**
* const context = this;
* const args = [...arguments];
* 这两行代码来获取 this 和 参数,是为了让 防抖(debounce) 函数最终返回的函数 this 指向不变以及依旧能接受到携带的参数。
*
*/
const context = this;
const args = [...arguments];
if (timeout) clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args)
}, wait);
}
}
/**
* content.onmousemove = count;
* 修改mousemove事件绑定的函数
* content.onmousemove = debounce(count,1000);
*
*/
content.onmousemove = debounce(count,1000);
运行效果
当鼠标在 div(灰色)区域中移动的时候不会触发执行函数,需要在停留在触发事件后函数 1 秒后才执行,而如果触发事件后的 1 秒内移动鼠标,则会重新计算函数执行时间。
防抖(debounce)立即执行版:
/**
* @description:
* @param {Function} func 事件函数
* @param {Number} wait 事件设置的延迟事件 (1000 = 1 秒)
* @return (Function): 返回新的 debounced(防抖动)函数。
*
*/
function debounce(func,wait) {
let timeout;
return function () {
const context = this;
const args = [...arguments];
if (timeout) clearTimeout(timeout);
const callNow = !timeout;
timeout = setTimeout(() => {
timeout = null;
}, wait)
if (callNow) func.apply(context, args)
}
}
/**
* content.onmousemove = count;
* 修改mousemove事件绑定的函数
* content.onmousemove = debounce(count,1000);
*
*/
content.onmousemove = debounce(count,1000);
运行结果
首次会运行一次。当鼠标在 div(灰色)区域中移动的时候不会触发执行函数,需要在停留在触发事件后函数 1 秒后才执行,而如果触发事件后的 1 秒内移动鼠标,则会重新计算函数执行时间。
防抖函数整合版
/**
* @desc 函数防抖
* @param func (function) 函数
* @param wait (number) 延迟执行毫秒数
* @param immediate (boolean) true 表立即执行,false 表非立即执行
* @return (Function): 返回新的 debounced(防抖动)函数。
*
*/
function debounce(func, wait, immediate) {
let timeout;
let result;
return function () {
const context = this;
const args = arguments;
if (timeout) clearTimeout(timeout);
if (immediate) {
const callNow = !timeout;
timeout = setTimeout(function () {
timeout = null;
}, wait)
if (callNow) result = func.apply(context, args)
}
else {
timeout = setTimeout(function () {
func.apply(context, args)
}, wait);
}
return result;
}
}
Lodash库防抖(debounce)函数
/**
* @desc 创建一个 debounced(防抖动)函数,该函数会从上一次被调用后,延迟 `wait` 毫秒后调用 `func` 方法。
* @param func (Function): 要防抖动的函数。
* @param [wait=0] (number): 需要延迟的毫秒数。
* @param [options={}] (Object): 选项对象。
* @param [options.leading=false] (boolean): 指定在延迟开始前调用。
* @param [options.maxWait] (number): 设置 func 允许被延迟的最大值。
* @param [options.trailing=true] (boolean): 指定在延迟结束后调用。
* @return (Function): 返回新的 debounced(防抖动)函数。
*
*/
_.debounce(func, [wait=0], [options={}])
_.debounce提供一个 options(选项) 对象决定如何调用 func
方法,主要是以下三个:
leading
,函数在每个等待时延的开始被调用,默认值为false
。trailing
,函数在每个等待时延的结束被调用,默认值是true
。maxwait
,最大的等待时间,因为如果debounce的函数调用时间不满足条件,可能永远都无法触发,因此增加了这个配置,保证大于一段时间后一定能执行一次函数。
leading
和trailing
的组合,可以实现不同的调用效果:
leading
-false
,trailing
-true
:默认情况,即在延时结束后才会调用函数。leading
-true
,trailing
-true
:在延时开始时就调用,延时结束后也会调用。leading
-true
,trailing
-false
:只在延时开始时调用。
节流(throttle)
连续触发事件但只会在规定时间内只执行一次函数。 节流会稀释函数的执行频率。
节流(throttle)时间戳版:
/**
* @desc 函数节流
* @param func (function) 函数
* @param wait (number) 延迟执行毫秒数
* @return (Function): 返回新的 throttle(防抖动)函数。
*
*/
function throttle(func, wait) {
var previous = 0;
return function() {
let now = Date.now();
let context = this;
let args = arguments;
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}
}
/**
* content.onmousemove = count;
* 修改mousemove事件绑定的函数
* content.onmousemove = throttle(count,1000);
*
*/
content.onmousemove = throttle(count,1000);
运行结果
当鼠标在 div(灰色)区域中移动的时候会持续触发事件,函数会立即执行,并且每 1s 执行一次。
节流(throttle)定时器版:
/**
* @desc 函数节流
* @param func (function) 函数
* @param wait (number) 延迟执行毫秒数
* @return (Function): 返回新的 throttle(防抖动)函数。
*
*/
function throttle(func, wait) {
let timeout;
return function() {
let context = this;
let args = arguments;
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
/**
* content.onmousemove = count;
* 修改mousemove事件绑定的函数
* content.onmousemove = throttle(count,1000);
*
*/
content.onmousemove = throttle(count,1000);
运行结果
当鼠标在 div(灰色)区域中移动的时候会持续触发事件,函数会立即执行,并且每 1s 执行一次。在停止触发事件后,函数还会再执行一次。
节流函数整合版:
/**
* @desc 函数节流
* @param func 函数
* @param wait 延迟执行毫秒数
* @param type 1 表时间戳版,2 表定时器版
* @return (Function): 返回新的 throttle(防抖动)函数。
*
*/
function throttle(func, wait ,type) {
if(type===1){
let previous = 0;
}else if(type===2){
let timeout;
}
return function() {
let context = this;
let args = arguments;
if(type===1){
let now = Date.now();
if (now - previous > wait) {
func.apply(context, args);
previous = now;
}
}else if(type===2){
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
func.apply(context, args)
}, wait)
}
}
}
}
Lodash库节流(throttle)函数
/**
* func (Function): 要节流的函数。
* @param [wait=0] (number): 需要节流的毫秒。
* @param [options={}] (Object): 选项对象。
* @param [options.leading=true] (boolean): 指定调用在节流开始前。
* @param [options.trailing=true] (boolean): 指定调用在节流结束后。
* @return (Function): 返回节流的函数
/
_.throttle(func, [wait=0], [options={}])
防抖(throttle)就是设置了maxwait
的节流(debounce)。防抖(throttle)同样提供了leading
和trailing
参数,与节流(debounce)含义相同。
_.throttle提供一个 options(选项) 对象决定如何调用 func
方法,主要是以下三个:
leading
,函数在每个等待时延的开始被调用,默认值为false
。trailing
,函数在每个等待时延的结束被调用,默认值是true
。
leading
和trailing
的组合,可以实现不同的调用效果:
leading
-false
,trailing
-true
:默认情况,即在延时结束后才会调用函数。leading
-true
,trailing
-true
:在延时开始时就调用,延时结束后也会调用。leading
-true
,trailing
-false
:只在延时开始时调用。
参考文章
https://github.com/mqyqingfeng/Blog/issues/22