js防抖与节流与方法封装
应用场景:
1、节流(Throttle):多次触发,固定时间间隔内只执行一次。
应用场景:比如窗口调整、页面滚动、抢购和疯狂点击等会用到节流。比如水龙头,不管开多久,单位时间内流出的水是固定的。
时机:周期性地执行
截流原理:在固定时间间隔内只执行一次回调,不管这段时间触发了多少次,只有第一次(或每间隔到期时)的执行有效,其余触发都被忽略或延迟到下一个周期。
2、防抖(Debounce):多次触发,变为最后一次触发后等待N秒后执行;比如百度搜索,就应该用防抖,当我连续不断输入时,不会发送请求;当我一段时间内不输入了,才会发送一次请求;如果小于这段时间继续输入的话,时间会重新计算,也不会发送请求。
时机:停止触发后才执行。
应用场景:搜索联想、表单校验、窗口resize后重新计算。比如电梯,有人进来就重置关门计时,直到没人进来才关门。
防抖核心原理:每当触发事件,就清掉之前的定时器,重新设置一个定时器,只有事件停止触发且等待时间到了,才执行回调。
3、总结:防抖是 “停下来才执行” ,截流时 ”每隔一段时间执行一次“。
deTh.js /** * 闭包函数 * * 防抖:对于短时间内连续触发的事件(滚动事件、表单重复提交、页面resize事件,常见于需要做页面适配的时候),让某个时间期限内,事件处理函数只执行一次。 * 节流:对于短时间内大量触发同一事件(滚动事件、输入框实时搜索),那么在函数执行一次之后,该函数在指定的时间期限内不再工作,直至过了这段时间才重新生效 */ export default { // 防抖 debounce: function (fn, delay) { // 时间期限 var delay = delay || 200; var timer; // 闭包 return function () { // 考虑作用域,上下文环境,apply需要用到this对象 var th = this; // 接收的参数用 ES6 中的 rest 参数统一存储到变量 args 中。arguments就是传入的参数数组,而且个数可以不确定的传回给fn(不确定函数到底有多少个参数,用arguments来接收) var args = arguments; // 判断还在定时,说明当前正在一个计时过程中,并且又触发了相同事件。所以要取消当前的计时,重新开始计时 if (timer) { clearTimeout(timer); } timer = setTimeout(function () { timer = null; // 执行方法 fn.apply(th, args); }, delay); }; }, // 节流 定时器 + 时间戳 throttle: function(func, delay) { var timer = null var startTime = Date.now() return function() { // 结束时间 var curTime = Date.now() // 间隔时间 = 延迟的时间 - (结束时间戳 - 开始时间戳) var interval = delay - (curTime - startTime) var th = this var args = arguments clearTimeout(timer) if(interval <= 0) { // 证明可以触发了 func.apply(th, args) // 重新计算开始时间 startTime = Date.now() } else { // 继续等待 timer = setTimeout(func, interval) } } } }4、TS泛型版防抖
// 泛型让防抖支持任意参数类型和返回值 type Fn = (...args: any[]) => any; interface DebouncedFunction<T extends Fn> { (...args: Parameters<T>): void; cancel: () => void; flush: () => void; } function debounce<T extends Fn>( fn: T, wait = 300, leading = false ): DebouncedFunction<T> { let timer: ReturnType<typeof setTimeout> | null = null; const debounced = function (this: any, ...args: Parameters<T>) { const invoke = () => fn.apply(this, args); if (timer) clearTimeout(timer); if (leading && timer === null) { invoke(); timer = setTimeout(() => { timer = null; }, wait); return; } timer = setTimeout(() => { invoke(); timer = null; }, wait); } as DebouncedFunction<T>; debounced.cancel = function () { if (timer) clearTimeout(timer); timer = null; }; debounced.flush = function () { if (timer) { clearTimeout(timer); debounced(); } }; return debounced; }5、TS泛型版截流
type Fn = (...args: any[]) => any; function throttle<T extends Fn>( fn: T, wait = 300, options: { leading?: boolean; trailing?: boolean } = {} ) { const { leading = true, trailing = true } = options; let timer: ReturnType<typeof setTimeout> | null = null; let lastTime = 0; return function (this: any, ...args: Parameters<T>) { const now = Date.now(); if (!leading && lastTime === 0) lastTime = now; if (now - lastTime >= wait) { if (timer) { clearTimeout(timer); timer = null; } fn.apply(this, args); lastTime = now; return; } if (!timer && trailing) { const remain = wait - (now - lastTime); timer = setTimeout(() => { fn.apply(this, args); lastTime = Date.now(); timer = null; }, remain); } }; }