前言
事件的觸發(fā)權(quán)很多時(shí)候都屬于用戶(hù)骡显,有些情況下會(huì)產(chǎn)生問(wèn)題:
1.向后臺(tái)發(fā)送數(shù)據(jù),用戶(hù)頻繁觸發(fā)兢哭,對(duì)服務(wù)器造成壓力
2.一些瀏覽器事件:window.onresize诅挑、window.mousemove等,觸發(fā)的頻率非常高甲棍,會(huì)造成瀏覽器性能問(wèn)題
如果你碰到這些問(wèn)題简识,那就需要用到函數(shù)節(jié)流和防抖了。
一感猛、函數(shù)節(jié)流(throttle)
函數(shù)節(jié)流:一個(gè)函數(shù)執(zhí)行一次后七扰,只有大于設(shè)定的執(zhí)行周期后才會(huì)執(zhí)行第二次。
有個(gè)需要頻繁觸發(fā)函數(shù)陪白,出于優(yōu)化性能角度颈走,在規(guī)定時(shí)間內(nèi),只讓函數(shù)觸發(fā)的第一次生效咱士,后面不生效立由。
1.如何實(shí)現(xiàn)
其原理是用時(shí)間戳來(lái)判斷是否已到回調(diào)該執(zhí)行時(shí)間,記錄上次執(zhí)行的時(shí)間戳序厉,然后每次觸發(fā) scroll 事件執(zhí)行回調(diào)锐膜,回調(diào)中判斷當(dāng)前時(shí)間戳距離上次執(zhí)行時(shí)間戳的間隔是否已經(jīng)到達(dá) 規(guī)定時(shí)間段,如果是弛房,則執(zhí)行枣耀,并更新上次執(zhí)行的時(shí)間戳,如此循環(huán)庭再;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<style>
html,
body {
height: 500%; /*讓其出現(xiàn)滾動(dòng)條*/
}
</style>
<body>
</body>
<script>
function throttle(fn,delay) {
// 記錄上一次函數(shù)觸發(fā)的時(shí)間
var lastTime= 0;
return function() {
// 記錄當(dāng)前函數(shù)觸發(fā)的時(shí)間
var nowTime=Date.now();
if(nowTime-lastTime>delay) {
// 修正this指向問(wèn)題
fn.call(this);
// 同步時(shí)間
lastTime=nowTime;
}
}
}
document.onscroll =throttle(function(){
console.log('scroll事件被觸發(fā)了'+ Date.now())
},1000)
</script>
</html>
//上例中用到了閉包的特性--可以使變量lastTime的值長(zhǎng)期保存在內(nèi)存中捞奕。
2.函數(shù)節(jié)流的應(yīng)用場(chǎng)景
需要間隔一定時(shí)間觸發(fā)回調(diào)來(lái)控制函數(shù)調(diào)用頻率:
DOM 元素的拖拽功能實(shí)現(xiàn)(mousemove)
搜索聯(lián)想(keyup)
計(jì)算鼠標(biāo)移動(dòng)的距離(mousemove)
Canvas 模擬畫(huà)板功能(mousemove)
射擊游戲的 mousedown/keydown 事件(單位時(shí)間只能發(fā)射一顆子彈)
監(jiān)聽(tīng)滾動(dòng)事件判斷是否到頁(yè)面底部自動(dòng)加載更多:給 scroll 加了 debounce 后,只有用戶(hù)停止?jié)L動(dòng)后拄轻,才會(huì)判斷是否到了頁(yè)面底部颅围;如果是 throttle 的話,只要頁(yè)面滾動(dòng)就會(huì)間隔一段時(shí)間判斷一次
二恨搓、函數(shù)防抖(debounce)
防抖函數(shù):一個(gè)需要頻繁觸發(fā)的函數(shù)院促,在規(guī)定時(shí)間內(nèi),只讓最后一次生效斧抱,前面的不生效常拓。
1.如何實(shí)現(xiàn)
其原理就第一次調(diào)用函數(shù),創(chuàng)建一個(gè)定時(shí)器辉浦,在指定的時(shí)間間隔之后運(yùn)行代碼弄抬。當(dāng)?shù)诙握{(diào)用該函數(shù)時(shí),它會(huì)清除前一次的定時(shí)器并設(shè)置另一個(gè)宪郊。如果前一個(gè)定時(shí)器已經(jīng)執(zhí)行過(guò)了掂恕,這個(gè)操作就沒(méi)有任何意義拖陆。然而,如果前一個(gè)定時(shí)器尚未執(zhí)行懊亡,其實(shí)就是將其替換為一個(gè)新的定時(shí)器依啰,然后延遲一定時(shí)間再執(zhí)行。
function debounce(fn,delay){
// 記錄上一次的延時(shí)器
var timer =null;
return function(){ // 清除上一次延時(shí)器
clearTimeout(timer)
timer =setTimeout(function(){fn.apply(this)},delay)
}
}
document.getElementById('btn').onclick =debounce(function(){
console.log('點(diǎn)擊事件被觸發(fā)'+Date.now())
},1000)
上例中也用到了閉包的特性--可以使變量timer的值長(zhǎng)期保存在內(nèi)存中店枣。
2.函數(shù)防抖的應(yīng)用場(chǎng)景
對(duì)于連續(xù)的事件響應(yīng)我們只需要執(zhí)行一次回調(diào):
1.每次 resize/scroll 觸發(fā)統(tǒng)計(jì)事件
2.文本輸入的驗(yàn)證(連續(xù)輸入文字后發(fā)送 AJAX 請(qǐng)求進(jìn)行驗(yàn)證速警,驗(yàn)證一次就好)
函數(shù)節(jié)流和函數(shù)去抖的核心其實(shí)就是限制某一個(gè)方法被頻繁觸發(fā),而一個(gè)方法之所以會(huì)被頻繁觸發(fā)鸯两,大多數(shù)情況下是因?yàn)?DOM 事件的監(jiān)聽(tīng)回調(diào)闷旧,而這也是函數(shù)節(jié)流以及防抖多數(shù)情況下的應(yīng)用場(chǎng)景。
總結(jié)
函數(shù)節(jié)流和函數(shù)去抖的核心其實(shí)就是限制某一個(gè)方法被頻繁觸發(fā)甩卓,而一個(gè)方法之所以會(huì)被頻繁觸發(fā)鸠匀,大多數(shù)情況下是因?yàn)?DOM 事件的監(jiān)聽(tīng)回調(diào)蕉斜,而這也是函數(shù)節(jié)流以及防抖多數(shù)情況下的應(yīng)用場(chǎng)景逾柿。