重要概念-防抖和节流 防抖和节流本质上是优化高频率执行代码 的手段。
当浏览器resize/scroll/keypress/mousemove等事件触发时,会不断地调用回调函数,极大地浪费资源、降低前端性能。
↑为了优化体验,需要对此类事件调用次数进行合理限制,这时候我们就可以使用防抖、节流的手段减少调用频率。
防抖-debounce
频繁操作 => 最后一次操作 定时器
定义 n秒后再执行该事件,如果在这期间被重复触发,那么重新计时。
栗子 电梯等人
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 function debounced (func, wait ) { let timeout; return function ( ) { let context = this ; let args = arguments ; clearTimeout (timeout); timeout = setTimeout (function ( ){ func.apply (context, args) }, wait); } } function debounced (func, wait ) { let timeout let flag return function ( ) { let context = this let args = arguments ; if (flag) { func.apply (context, args) flag = false } clearTimeout (timeout) timeout = setTimeout (function ( ) { flag = true }, wait) } } function debounced (func, wait, immediate ) { let timeout return function ( ) { let context = this let args = arguments if (timeout) clearTimeout (timeout) if (immediate) { let callNow = !timeout timeout = setTimeout (function ( ) { timeout = null }, wait) if (callNow) { func.apply (context, args) } } else { timeout = setTimeout (function ( ) { func.apply (context, args) }, wait) } } } function debounce (func, wait, immediate ) { let timeout; function debounced ( ) { const context = this ; const args = arguments ; if (timeout) clearTimeout (timeout); if (immediate) { const callNow = !timeout; timeout = setTimeout (() => { timeout = null ; }, wait); if (callNow) { return func.apply (context, args); } } else { timeout = setTimeout (() => { func.apply (context, args); }, wait); } } debounced.cancel = function ( ) { clearTimeout (timeout); timeout = null ; }; return debounced; }
使用 1 2 3 4 5 6 7 8 9 10 function myFunc ( ) { ....我的函数 } function debounce ( ) { ....防抖函数 } let debounceMyfunc = debounce (myFunc, 300 , ...其他参数)debounceMyfunc ()debounceMyfunc.cancel ()
场景 防抖在连续的事件,只需触发一次回调的场景有:
搜索框搜索输入。只需用户最后一次输入完,再发送请求。
手机号、邮箱验证输入检测。
窗口大小resize
。只需窗口调整完成后,计算窗口大小。防止重复渲染。
节流-throttle
频繁操作 => 少量操作 闭包+延迟器
定义 n秒内只运行一次,如果在这期间重复触发,那么只有一次生效。
栗子 电梯送人
实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 function throttled (func, delay ) { let oldTime = Date .now () return function ( ) { let context = this let args = arguments let newTime = Date .now () if (newTime - oldTime >= delay) { func.apply (context, args) oldTime = Date .now () } } } function throttled (func, delay ) { let timer = null return function ( ) { let context = this let args = arguments if (!timer) { timer = setTimeout (function ( ) { func.apply (context, args) timer = null }, delay) } } } function throttled (func, delay ) { let timer = null let startTime = Date .now () return function ( ) { let curTime = Date .now () let context = this let args = arguments let remaining = delay - (curTime - startTime) clearTimeout (timer) if (remaining <= 0 ) { func.apply (context, args) startTime = Date .now () } else { timer = setTimeout (func, remaining) } } }
使用 1 2 3 4 5 6 7 8 function handleScroll (event ) { ....滚动事件处理代码 } function throttled (func, delay ) { ....节流函数 } window .addEventListener ('scroll' , throttled (handleScroll, 500 ));
场景 节流在间隔一段时间执行一次回调的场景有:
滚动加载,加载更多或滚到底部监听
搜索框,搜索联想功能
两者的异同 同:
都是为了较低回调的执行频率,节省计算资源
都可以使用定时器实现
异:
防抖着重于一定时间连续触发的事件中只执行最后一次;节流着重于一段时间仅执行一次
防抖是在一段连续操作后 执行一次; 节流是每一段时间只 执行一次