防抖和节流的提出是为了解决由于某些频繁事情的发生导致对系统性能的损耗。
手写防抖函数
查看
对于防抖的实现过程为:
-
当触发一个事件时,相应的函数并不会立刻执行,而是会等待一定时间再执行。
-
当频繁触发一个事件的,相应的函数将会被频繁的推迟。
-
当停止触发该事件,且在等待时间内,不再触发该事件,其相应的函数才会执行一次。
在JS中防抖的应用场景包括:
-
对输入框的的输入监听。
-
对DOM(按钮等)的频繁操作。
-
对浏览器滚动条的事件监听。
-
对浏览器缩放resize的监听。
-
…
防抖函数的基础代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <input type="text" class="input"> <script> function debounce(fn, delay) { let timer = null function _debounce() { if (timer) clearTimeout(timer) timer = setTimeout(() => { fn() }, delay) } return _debounce }
const inputEl = document.querySelector('.input')
inputEl.oninput = debounce(() => { console.log(inputEl.value) }, 1000) </script>
|
接下来对其进行优化加入更多要求:
优化参数和this指向
1 2 3 4 5 6 7 8
|
function _debounce(...args) { if (timer) clearTimeout(timer) timer = setTimeout(() => { fn.apply(this, args) }, delay) }
|
优化取消操作(增加取消功能)
1 2 3 4 5 6
| _debounce.cancel = function () { clearTimeout(timer) timer = null }
|
优化立即执行效果(第一次立即执行)
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
| function debounce(fn, delay, immediate = true) { let timer = null let isInvoke = false function _debounce(...args) {
if (timer) clearTimeout(timer)
if (!isInvoke && immediate) { fn.apply(this, args) isInvoke = true return }
timer = setTimeout(() => { fn.apply(this, args) isInvoke = false timer = null }, delay) } _debounce.cancel = function () { if (timer) clearTimeout(timer) timer = null } return _debounce }
const inputEl = document.querySelector('.input') const btnEl = document.querySelector('.btn') const debounceFn = debounce((event) => { console.log(event.target.value) }, 1000)
inputEl.oninput = debounceFn btnEl.onclick = debounceFn.cancel
|
优化防抖返回值
对于获取其返回值有两种方法,第一种为传入一个回调函数,通过回调函数获取返回值,第二种方式则是通过使用Promise来获取返回值。这里展示第二种方案。
如果有其他定制化操作,也可自行添加,但核心也就是下面这样了。
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
| function debounce(fn, delay, immediate = true) { let timer = null let isInvoke = false function _debounce(...args) {
return new Promise((resolve, reject) => { try { if (timer) clearTimeout(timer)
if (!isInvoke && immediate) { const result = fn.apply(this, args) resolve(result) isInvoke = true return }
timer = setTimeout(() => { const result = fn.apply(this, args) resolve(result) isInvoke = false timer = null }, delay) } catch (error) { reject(error) } }) } _debounce.cancel = function () { if (timer) clearTimeout(timer) timer = null } return _debounce }
|
手写节流函数
查看
对于节流的实现过程为:
对于节流的引用场景包括:
-
监听页面滚动事件。
-
鼠标移动事件。
-
用户频繁点击按钮操作。
-
游戏中的一些设计(如飞机的子弹发射)。
-
…
节流函数的基础代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| function throttle(fn, delay) { let satrtTime = 0 function _throttle() { let nowTime = Date.now() let waitTime = delay - (nowTime - satrtTime) if (waitTime <= 0) { fn() satrtTime = nowTime } }
return _throttle }
const inputEl = document.querySelector('.input') const btnEl = document.querySelector('.btn') const throttleFn = throttle((event) => { console.log(inputEl.value) }, 1000)
inputEl.oninput = throttleFn
|
优化参数、this指向、节流第一次是否可以执行和最后一次可以执行
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
| function throttle(fn, interval, { leading = true, trailing = false } = {}) { let startTime = 0 let timer = null function _throttle(...args) {
let nowTime = Date.now() if (!leading && startTime === 0) { startTime = nowTime } let waitTime = interval - (nowTime - startTime) if (waitTime <= 0) { if (timer) clearTimeout(timer) fn.apply(this, args) startTime = nowTime timer = null return }
if (trailing && !timer) { timer = setTimeout(() => { fn.apply(this, args) startTime = Date.now() timer = null }, waitTime) }
}
return _throttle }
|
添加取消功能(取消最后一次执行)
1 2 3 4 5
| _throttle.cancel = function () { if (timer) clearTimeout(timer) startTime = 0 timer = null }
|
节流返回值
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
| function throttle(fn, interval, { leading = false, trailing = true } = {}) { let startTime = 0 let timer = null function _throttle(...args) { return new Promise((resolve, reject) => { try { let nowTime = Date.now() if (!leading && startTime === 0) { startTime = nowTime } let waitTime = interval - (nowTime - startTime) if (waitTime <= 0) { if (timer) clearTimeout(timer) const result = fn.apply(this, args) resolve(result) startTime = nowTime timer = null return }
if (trailing && !timer) { timer = setTimeout(() => { const result = fn.apply(this, args) resolve(result) startTime = Date.now() timer = null }, waitTime) }
} catch (error) { reject(error) } })
} _throttle.cancel = function () { if (timer) clearTimeout(timer) startTime = 0 timer = null }
return _throttle }
|