防抖
指触发事件后在规定时间后执行一次。如果在规定时间内又执行了,则会重新计算规定时间
理解:公交站等车,规定需要等到10分钟发车,但是如果10分钟内一直有人上车,那么需要重新等待10分钟,直到这10分钟内没有人上车,就发车。
实现思路:
1. 事件第一次触发时,timeout为null,调用later后,若immedaite为true,那么立即调用fn。如果immediate为false,那么经过wait秒后,调用fn
2.事件第二次触发,如果timeout为null,就执行第一步。如果不为null,即在wait时间内又触发这个时候需要清空定时器,重新开始计时。
function debounce(fn, wait, immediate) {
let timeout;
const later = function(context, args) {
setTimeout(() => {
timeout = null;
if (!immediate) {
fn.call(context, args);
context = null;
args = null;
}
}, wait);
};
const debounce = function(... args) {
if (!timeout) {
timeout = later(this, args);
if (immediate) {
fn.apply(this, args);
}
} else {
clearTimeout(timeout);
timeout = later(this, args);
}
};
debounce.cancel = () => {
clearTimeout(timeout);
timeout = null;
};
return debounce;
}
function handle() {
console.log(Math.random());
}
window.addEventListener("mousemove", debounce(handle, 1000, true)); // 调用立即执行版本
window.addEventListener("mousemove", debounce(handle, 1000, false)); // 调用非立即执行版本节流
在规定的时间内只执行一次,如果这个时间内多次触发,只能有一次生效
理解: 公交车站,需要等到10分钟发车,不管这10分钟有多少乘客,只要到了10分钟,就发车
立即执行
使用时间戳:
//立即执行function throttle(fn, wait) {
let previous = 0;
return function(... args) {
const now = new Date();
if (now - previous > wait) {
fn.apply(this, args);
previous = now;
}
};
}非立即执行
使用定时器:
//非立即执行,第一次不会立马执行,但是会执行最后一次
function throttle(fn, wait) {
let timeout;
return function(... args) {
if (timeout) {
return false;
}
timeout = setTimeout(() => {
fn.apply(this, args);
timeout = null;
}, wait);
};
}立即执行&非立即执行
实现思路:
1. 第一次执行时候,需要立马执行。 这里重要点就是wait - (now-previous)这个remaining时间。第一次remaing肯定是小于0的。这个时候就需要立马执行函数fn,如果这个时候有定时器,就需要清空定时器,然后执行fn函数
2. 第二次执行,如果remaing时间小于0,就执行第一步。如果remaing 大于0 ,这个时候判断是否有定时器,如果有,不处理,等待执行就好,如果没有就重新定义个定时器。
function throttle(fn, wait) {
let previous = 0;
let remaining; let timeout;
const throttle = function(... args) {
const now = +new Date();
remaining = wait - (now - previous);
if (remaining < 0) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
fn.apply(this, ... args);
} else {
if (!timeout) {
timeout = setTimeout(() => {
timeout = null;
previous = +new Date();
fn.apply(this, ... args);
}, remaining);
}
}
};
throttle.cancel = function() {
previous = 0;
clearTimeout(timeout);
timeout = null;
};
return throttle;
}
function handle() {
console.log(Math.random());
}
window.addEventListener("mousemove", throttle(handle, 1000));优化
添加参数控制首尾是否执行
function throttle(fn, wait, options = {}) {
let previous = 0;
let remaining; let timeout;
const { leading, trailing } = options;
const isLeading = typeof leading === "boolean" ? leading : true;
const isTrailing = typeof trailing === "boolean" ? trailing : true;
const throttle = function(... args) {
const now = +new Date();
if (!isLeading && !previous) {
previous = now;
}
remaining = wait - (now - previous);
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
fn.apply(this, ... args);
} else {
if (!timeout && isTrailing) {
timeout = setTimeout(() => {
timeout = null;
previous = !isLeading ? 0 : +new Date();
fn.apply(this, ... args);
}, remaining);
}
}
};
throttle.cancel = function() {
previous = 0;
clearTimeout(timeout);
timeout = null;
};
return throttle;
}
function handle() {
console.log(Math.random());
}
window.addEventListener("mousemove", throttle(handle, 1000));