JavaScript 防抖和節(jié)流詳解
防抖
自動(dòng)門感應(yīng)到有人,打開門,并且開始5秒倒計(jì)時(shí),在 5 s 內(nèi)有另外一個(gè)人靠近到門,門感應(yīng)到人,重新5秒倒計(jì)時(shí)
當(dāng)事件被觸發(fā)時(shí),設(shè)定一個(gè)延遲,若期間事件又被觸發(fā),則重新設(shè)定延遲,直到延遲結(jié)束,執(zhí)行動(dòng)作 (防止多次觸發(fā))
web 應(yīng)用上面
- 改變頁面大小的統(tǒng)計(jì)
- 滾動(dòng)頁面位置的統(tǒng)計(jì)
- 輸入框連續(xù)輸入的請(qǐng)求次數(shù)控制
一開始,點(diǎn)擊按鈕,console.log('pay money')
<body> <button id="btn">click</button> </body> <script> const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } btn.addEventListener('click', payMoney) </script>
定義 debounce
const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func) { // 在函數(shù)里面返回函數(shù) , 只有當(dāng)點(diǎn)擊的時(shí)候才返回該函數(shù) return function () { func() } } btn.addEventListener('click', debounce(payMoney))
設(shè)置延時(shí)
const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func, delay) { return function () { setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))
清除延時(shí):未能執(zhí)行
原因
每次點(diǎn)擊的時(shí)候就會(huì)執(zhí)行返回函數(shù)里面的內(nèi)容
每次點(diǎn)擊的執(zhí)行函數(shù)都是獨(dú)立的,互不干涉
正因?yàn)樗麄冎g沒有聯(lián)系,清除延時(shí)在這里完全沒有起作用
要讓這些獨(dú)立的執(zhí)行函數(shù)之間有聯(lián)系,需要用到作用域鏈(閉包)
const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func, delay) { return function () { let timer clearInterval(timer) timer = setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))
將 timer 放在返回函數(shù)的外圍,這樣在定義監(jiān)聽事件的同時(shí),就定義了 timer
變量
因?yàn)樽饔糜蜴?,所有?dú)立的執(zhí)行函數(shù)都能訪問到這個(gè)timer變量
而且現(xiàn)在這個(gè)timer變量只創(chuàng)建了一次。是唯一的,我們只不過不斷給timer賦值進(jìn)行延時(shí)而已
每個(gè)清除延時(shí)就是清除上一個(gè)定義的延時(shí),相當(dāng)于多個(gè)函數(shù)共用同一個(gè)外部變量
const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); } function debounce(func, delay) { let timer return function () { clearInterval(timer) timer = setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))
此時(shí)的代碼,this 是指向 window ?
因?yàn)榛卣{(diào)的原因,運(yùn)行時(shí)已經(jīng)在Window下了
const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); console.log(this); } function debounce(func, delay) { let timer return function () { clearInterval(timer) timer = setTimeout(_ => func(), delay) } } btn.addEventListener('click', debounce(payMoney, 1000))
解決辦法
在 setTimeout 之前將 this 保存下來,此時(shí)的 this 是指向按鈕的
const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); console.log(this); } function debounce(func, delay) { let timer // 只有當(dāng)點(diǎn)擊的時(shí)候,才返回此函數(shù),所以 this 是指向按鈕的 return function () { let context = this clearInterval(timer) timer = setTimeout(_ => { func.apply(context) }, delay) } } btn.addEventListener('click', debounce(payMoney, 1000))
考慮參數(shù)的問題,添加 arg
const btn = document.getElementById('btn') function payMoney() { console.log('pay money'); console.log(this); } function debounce(func, delay) { let timer return function () { let context = this let args = arguments clearInterval(timer) timer = setTimeout(_ => { func.apply(context) console.log(context, args); }, delay) } } btn.addEventListener('click', debounce(payMoney, 1000))
節(jié)流
先觸發(fā)一次后,防止接下來多次觸發(fā)
滾動(dòng)屏幕:統(tǒng)計(jì)用戶滾動(dòng)屏幕的行為來作出相應(yīng)的網(wǎng)頁反應(yīng)
當(dāng)用戶不斷進(jìn)行滾動(dòng),就會(huì)不斷產(chǎn)生請(qǐng)求,相應(yīng)也會(huì)不斷增加,容易導(dǎo)致 ⛔️ 網(wǎng)絡(luò)阻塞
我們就可以在觸發(fā)事件的時(shí)候就馬上執(zhí)行任務(wù),然后設(shè)定時(shí)間間隔限制,在這段時(shí)間內(nèi)不管用戶如何進(jìn)行滾動(dòng)都忽視操作
在時(shí)間到了以后如果監(jiān)測到用戶有滾動(dòng)行為,再次執(zhí)行任務(wù)。并且設(shè)置時(shí)間聞隔
首先,寫個(gè)改變頁面尺寸的同時(shí)改變頁面背景顏色的代碼
function coloring() { let r = Math.floor(Math.random() * 255) let g = Math.floor(Math.random() * 255) let b = Math.floor(Math.random() * 255) document.body.style.background = `rgb(${r}, ${g}, $)` } window.addEventListener('resize', coloring)
function throttle(func, delay) { let timer return function () { timer = setTimeout(_ => { func() }, delay) } } window.addEventListener('resize', throttle(coloring, 2000))
判斷觸發(fā)的事件是否在時(shí)間間隔內(nèi)
- 不在:觸發(fā)事件
- 在:不觸發(fā)事件
function throttle(func, delay) { let timer return function () { // timer 被賦值了,直接返回,即不執(zhí)行任務(wù) if (timer) { return } // 此時(shí) timer 沒被賦值,或 timer 已經(jīng)執(zhí)行完了 // 為 timer 賦值進(jìn)行延時(shí)執(zhí)行 timer = setTimeout(_ => { func() // 延遲執(zhí)行以后我們要清空timer的值 timer = null }, delay) } } window.addEventListener('resize', throttle(coloring, 2000))
解決 this 指向(雖然當(dāng)前的這個(gè)例子就是在 Window 下的)
function throttle(func, delay) { let timer return function () { let context = this let args = arguments // timer 被賦值了,直接返回,即不執(zhí)行任務(wù) if (timer) { return } // 此時(shí) timer 沒被賦值,或 timer 已經(jīng)執(zhí)行完了 // 為 timer 賦值進(jìn)行延時(shí)執(zhí)行 timer = setTimeout(_ => { func.apply(context, args) // 延遲執(zhí)行以后我們要清空timer的值 timer = null }, delay) } } window.addEventListener('resize', throttle(coloring, 1000))
節(jié)流核心:事件間隔 另一種常見的時(shí)間間隔就是用Date對(duì)象
function throttle(func, delay) { // 我們要和前一個(gè)時(shí)間點(diǎn)進(jìn)行比較才能確定是否已經(jīng)過了時(shí)間間隔 // 在返回函數(shù)外圍,避免每次執(zhí)行都被自動(dòng)修改 let pre = 0 return function () { // 保存執(zhí)行函數(shù)當(dāng)時(shí)的時(shí)間 let now = new Date() // 剛開始,一定會(huì)執(zhí)行 if (now - pre > delay) { // 已經(jīng)過了時(shí)間間隔,就可以執(zhí)行函數(shù)了 func() // 執(zhí)行后,重新設(shè)置間隔點(diǎn) pre = now } } } window.addEventListener('resize', throttle(coloring, 1000))
解決參數(shù)問題
function throttle(func, delay) { let pre = 0 return function () { let context = this let args = arguments let now = new Date() if (now - pre > delay) { func.apply(context, args) pre = now } } } window.addEventListener('resize', throttle(coloring, 1000))
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
- JavaScript函數(shù)防抖動(dòng)debounce
- 通過實(shí)例講解JS如何防抖動(dòng)
- JavaScript運(yùn)動(dòng)框架 解決防抖動(dòng)問題、懸浮對(duì)聯(lián)(二)
- JavaScript中防抖和節(jié)流的區(qū)別及適用場景
- JavaScript中防抖和節(jié)流的實(shí)戰(zhàn)應(yīng)用記錄
- JavaScript深入理解節(jié)流與防抖
- JavaScript防抖與節(jié)流的實(shí)現(xiàn)與注意事項(xiàng)
- JavaScript的防抖和節(jié)流一起來了解下
- JavaScript中函數(shù)的防抖與節(jié)流詳解
- javascript的防抖和節(jié)流你了解嗎
- 淺談JavaScript節(jié)流與防抖
- 關(guān)于JavaScript防抖與節(jié)流的區(qū)別與實(shí)現(xiàn)
- JavaScript防抖與節(jié)流詳解
- JavaScript防抖動(dòng)與節(jié)流處理
相關(guān)文章
JavaScript引用類型Function實(shí)例詳解
這篇文章主要介紹了JavaScript引用類型Function,結(jié)合實(shí)例形式詳細(xì)分析了javascript引用類型Function概念、定義、原理、相關(guān)使用技巧與操作注意事項(xiàng),需要的朋友可以參考下2018-08-08JS+CSS實(shí)現(xiàn)可拖動(dòng)的彈出提示框
這篇文章主要介紹了JS+CSS實(shí)現(xiàn)可拖動(dòng)的彈出提示框,涉及針對(duì)鼠標(biāo)事件及html元素的操作技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2015-02-02微信小程序?qū)崿F(xiàn)跑馬燈效果完整代碼(附效果圖)
這篇文章主要介紹了微信小程序?qū)崿F(xiàn)跑馬燈效果完整代碼(附效果圖),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05JavaScript中的Map數(shù)據(jù)結(jié)構(gòu)詳解
這篇文章主要為大家介紹了JavaScript的Map數(shù)據(jù)結(jié)構(gòu),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-01-01javascript 實(shí)現(xiàn)父窗口引用彈出窗口的值的腳本
javascript 實(shí)現(xiàn)父窗口引用彈出窗口的值的腳本...2007-08-08深入理解JavaScript 中的匿名函數(shù)((function() {})();)與變量的作用域
匿名函數(shù)沒有實(shí)際名字的函數(shù),匿名函數(shù)(function() {})();是一個(gè)特殊的閉包寫法。本文蛀牙給大家介紹JavaScript 中的匿名函數(shù)((function() {})();)與變量的作用域,需要的朋友可以參考下2018-08-08原生javascript實(shí)現(xiàn)圖片按鈕切換
這篇文章主要介紹了原生javascript實(shí)現(xiàn)圖片按鈕切換,需要的朋友可以參考下2015-01-01