关于防抖和节流:
在频繁的事件回调中做复杂计算,很有可能造成页面卡顿,此时可以将多次计算合并为一次计算,只在一个精确点做操作。
防抖和节流都是防止函数多次调用 区别在于:
防抖会在每次触发后重新计时,直到到达指定时间间隔
节流是相隔指定时间再执行
防抖:
其实防抖的原理很简单,看完下面这个生活应用场景就已经非常清晰了
生活中最常见的就是电梯,关门机制:当有人进入电梯就重新计时等待指定的间隔时间。
防抖就是只要有事件进来就重新计时,如果一直触发这个事件并且间隔小于指定时间间隔,就一直不能执行,
只有这次事件进来后重新计时并且在指定间隔时间内没有事件再进来才会执行。
实现防抖:
// 简单版防抖
/*
* func 传入的需要防抖的函数
* wait 等待时间
*/
const debounce_one = (func, wait = 50) => {
// 缓存一个定时器id
let timer = 0;
// 这里返回的函数是每次用户实际调用的防抖函数
// 如果已经设定过定时器了就清空上一次的定时器
// 开始一个新的定时器,延迟执行用户传入的方法
return function (...args) {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
func.apply(this, args)
}, wait)
}
}
简单版的不支持函数内部使用this和event。
// 最终版:支持this和event 并且考虑函数有返回值 支持取消 支持立即执行
/*
* func 传入的需要防抖的函数
* wait 等待时间
* immediate 是否立即执行
*/
function debounce(func, wait, immediate) {
var timeout, result;
var debounced = function() {
var context = this; // 保存this,在在防抖函数中支持this
var args = arguments; // 保存参数
// 如果定时器存在,表示还未到达间隔时间,清除定时器,开始一个新的定时器
if(timeout) clearTimeout(timeout);
// 立即执行
if(immdiate) {
var callNow = !timeout; // callNow
timeout = setTimeout(function() {
timeout = null; // 到达时间后清空
}, wait)
if(callNow) result = func.apply(context, args);
} else {
timeout = setTimeout(function() {
func.apply(context, args);
}, wait)
}
return result;
}
// 取消防抖,即在间隔时间没有结束时 可以手动取消,清除定时器
debounced.cancel = function() {
clearTimeout(timeout);
timeout = null;
}
return debounced;
}
// 测试
var count = 1
function handle(){
console.log(count++);
}
window.onmousemove = debounce(handle, 2000, true);
节流:
节流:规定一个单位时间,在单位时间内只能执行一次,如果多次触发事件,只能有一次生效。
通常通过节流来降低事件调用的频率。
// 简单版
function throttle(func, wait = 200) {
let timer = 0;
return function () {
let context = this;
let args = arguments;
if (!timer) {
timer = setTimeout(() => {
func(context, args)
timer = 0;
clearTimeout(timer)
}, wait)
}
}
}
// 测试
function handle() {
console.log(Math.randon());
}
window.addEventLitener("mousemove", throttle(handle, 300));
// 最终版 支持this event
/*
* func 执行节流的函数
* wait 间隔时间
* options 对象参数 包含属性:
* leading: false 取消执行第一次
* trailing: false 取消执行最后一次
*/
function throttle(func, wait, options) {
var timeout, context, args, result;
// 记录执行时间
var previous = 0;
if (!options) options = {};
// 延时执行函数
var later = function () {
previous = options.leading === false ? 0 : new Date().getTime();
timeout = null;
result = func.apply(context, args);
if (!timeout) context = args = null;
}
var throttled = function () {
var now = new Date().getTime();
// 判断上次是否执行和第一次是否执行
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.cancle = function () {
clearTimeout(timeout);
previous = 0;
timeout = null;
}
return throttled;
}
// 测试
function handle() {
console.log(Math.random());
}
window.addEventListener('mousemove', throttle(handle, 2000, {leading: false}));
学习JavaScript专题之跟着underscore学防抖记录本文