溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

JavaScript中如何實(shí)現(xiàn)防抖和節(jié)流

發(fā)布時(shí)間:2021-09-28 09:15:37 來源:億速云 閱讀:146 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要為大家展示了“JavaScript中如何實(shí)現(xiàn)防抖和節(jié)流”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“JavaScript中如何實(shí)現(xiàn)防抖和節(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)測(cè)到用戶有滾動(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))

以上是“JavaScript中如何實(shí)現(xiàn)防抖和節(jié)流”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI