有时候我们想在用户进行某些操作之后向服务端要一些数据,但有些事件的触发是很频繁的,例如mousemove、scroll等。这时就会希望不会频繁地去触发回调函数,而是在一定时间内只触发一次。这时就需要用到防抖和节流。
防抖 debounce
防抖分为尾部执行及立即执行两种
尾部执行
尾部执行.png即事件触发后延迟一定时间再执行回调函数,期间若有事件触发,则重新计时。
第一版
function debounce(fn, wait) {
let timeout;
return function(){
if(timeout){
clearTimeout(timeout);
}
timeout =setTimeout(function() {
timeout = fn();
}, wait);
}
}
希望调用回调函数时可以传参,以及保持this指向,于是有
第二版
function debounce(fn, wait) {
let timeout;
return function () {
const context = this;
const args = arguments;
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(function () {
timeout = fn.apply(context, args);
}, wait);
}
}
立即执行
立即执行.png即事件触发时马上执行回调函数,在一定时间内不再执行回调函数,若期间有事件触发,则重新计时。
function debounce(fn, wait) {
let timeout;
return function(){
const context = this;
const args = arguments;
if(!timeout){
fn.apply(context, args);
timeout = setTimeout(function() {
timeout = null;
}, wait);
}else{
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
}, wait);
}
}
}
结合两种方式
function debounce(fn, wait, immediate) {
let timeout;
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) {
fn.apply(context, args);
}
} else {
timeout = setTimeout(function () {
timeout = fn.apply(context, args);
}, wait);
}
}
}
增加取消功能,当想要在不再执行回调函数的时间内,取消等待,重置状态。
最终版防抖
function debounce(fn, wait, immediate) {
let timeout;
const debounced = function () {
const context = this;
const args = arguments;
if (timeout) {
clearTimeout(timeout);
}
if (immediate) {
const callNow = !timeout;
timeout = setTimeout(function () {
timeout = null;
}, wait);
if (callNow) {//立即执行的情况马上返回执行结果
return fn.apply(context, args);
}
} else {
timeout = setTimeout(function () {
timeout = fn.apply(context, args);
}, wait);
}
}
//增加取消
debounced.cancel = function () {
clearTimeout(timeout);
timeout = null;
}
return debounced;
}
节流 throttle
节流分为leading和trailing两种
leading
leading.png在触发事件时马上进行一次回调执行,并在固定时间内不再进行调用。
function throttle(fn, wait) {
let timeout;
return function () {
const context = this;
const args = arguments;
if (!timeout) {
fn.apply(context, args);
timeout = setTimeout(function () {
timeout = null;
}, wait);
}
}
}
trailing
trailing.png在触发事件的一定时间后执行回调函数,期间的事件不会造成任何影响。
···
function throttle(fn, wait) {
let timeout;
return function () {
const context = this;
const args = arguments;
if (!timeout) {
timeout = setTimeout(function () {
fn.apply(context, args);
timeout = null;
}, wait);
}
}
}
···
合并一下两者
function throttle(fn, wait, options) {
let timeout;
options = options ? options : {};
return function () {
const context = this;
const args = arguments;
if (!timeout) {
if(options.leading !== false){
fn.apply(context, args);
}
timeout = setTimeout(function () {
if(options.trailing !== false){
fn.apply(context, args);
}
if(options.leading !== false){
//当leading和trailing都开启的时候 保证尾部调用跟下一次调用之间有一个间隔
setTimeout(function() {
timeout = null;
}, wait);
}else{
timeout = null;
}
}, wait);
}
}
}
同样加上取消操作
function throttle(fn, wait, options) {
let timeout1;
let timeout2;
options = options ? options : {};
const throttle = function () {
const context = this;
const args = arguments;
if (!timeout1) {
if(options.leading !== false){
fn.apply(context, args);
}
timeout1 = setTimeout(function () {
if(options.trailing !== false){
fn.apply(context, args);
}
if(options.leading !== false){
//当leading和trailing都开启的时候 保证尾部调用跟下一次调用之间有一个间隔
timeout2 = setTimeout(function() {
timeout1 = null;
}, wait);
}else{
timeout1 = null;
}
}, wait);
}
}
throttle.cancel = function(){
clearTimeout(timeout1);
clearTimeout(timeout2);
timeout1 = timeout2 = null;
}
}
总结
节流是一定时间内,回调函数只执行一次。
防抖是一定时间内,回调函数只执行一次,但期间若有事件触发,则重新计时。
如果遇到的是连续不停的事件,则应该选择节流,因为不停触发的事件会让防抖只会执行一次回调函数
网友评论