参考答案:
本质上是优化高频率执行代码的一种手段
如:浏览器的 resize
、scroll
、keypress
、mousemove
等事件在触发时,会不断地调用绑定在事件上的回调函数,极大地浪费资源,降低前端性能
为了优化体验,需要对这类事件进行调用次数的限制,对此我们就可以采用throttle
(节流)和debounce
(防抖)的方式来减少调用频率
一个经典的比喻:
想象每天上班大厦底下的电梯。把电梯完成一次运送,类比为一次函数的执行和响应
假设电梯有两种运行策略 debounce
和 throttle
,超时设定为15秒,不考虑容量限制
电梯第一个人进来后,15秒后准时运送一次,这是节流
电梯第一个人进来后,等待15秒。如果过程中又有人进来,15秒等待重新计时,直到15秒后开始运送,这是防抖
完成节流可以使用时间戳与定时器的写法
使用时间戳写法,事件会立即执行,停止触发后没有办法再次执行
1function throttled1(fn, delay = 500) { 2 let oldtime = Date.now() 3 return function (...args) { 4 let newtime = Date.now() 5 if (newtime - oldtime >= delay) { 6 fn.apply(null, args) 7 oldtime = Date.now() 8 } 9 } 10} 11
使用定时器写法,delay
毫秒后第一次执行,第二次事件停止触发后依然会再一次执行
1function throttled2(fn, delay = 500) { 2 let timer = null 3 return function (...args) { 4 if (!timer) { 5 timer = setTimeout(() => { 6 fn.apply(this, args) 7 timer = null 8 }, delay); 9 } 10 } 11}
可以将时间戳写法的特性与定时器写法的特性相结合,实现一个更加精确的节流。实现如下
1function throttled(fn, delay) { 2 let timer = null 3 let starttime = Date.now() 4 return function () { 5 let curTime = Date.now() // 当前时间 6 let remaining = delay - (curTime - starttime) // 从上一次到现在,还剩下多少多余时间 7 let context = this 8 let args = arguments 9 clearTimeout(timer) 10 if (remaining <= 0) { 11 fn.apply(context, args) 12 starttime = Date.now() 13 } else { 14 timer = setTimeout(fn, remaining); 15 } 16 } 17}
简单版本的实现
1function debounce(func, wait) { 2 let timeout; 3 4 return function () { 5 let context = this; // 保存this指向 6 let args = arguments; // 拿到event对象 7 8 clearTimeout(timeout) 9 timeout = setTimeout(function(){ 10 func.apply(context, args) 11 }, wait); 12 } 13}
防抖如果需要立即执行,可加入第三个参数用于判断,实现如下:
1function debounce(func, wait, immediate) { 2 3 let timeout; 4 5 return function () { 6 let context = this; 7 let args = arguments; 8 9 if (timeout) clearTimeout(timeout); // timeout 不为null 10 if (immediate) { 11 let callNow = !timeout; // 第一次会立即执行,以后只有事件执行后才会再次触发 12 timeout = setTimeout(function () { 13 timeout = null; 14 }, wait) 15 if (callNow) { 16 func.apply(context, args) 17 } 18 } 19 else { 20 timeout = setTimeout(function () { 21 func.apply(context, args) 22 }, wait); 23 } 24 } 25}
相同点:
setTimeout
实现不同点:
clearTimeout
和 setTimeout
实现。函数节流,在一段连续操作中,每一段时间只执行一次,频率较高的事件中使用来提高性能例如,都设置时间频率为500ms,在2秒时间内,频繁触发函数,节流,每隔 500ms 就执行一次。防抖,则不管调动多少次方法,在2s后,只会执行一次
如下图所示:
防抖在连续的事件,只需触发一次回调的场景有:
resize
。只需窗口调整完成后,计算窗口大小。防止重复渲染。节流在间隔一段时间执行一次回调的场景有:
最近更新时间:2024-07-22