iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 9
0
Modern Web

從技術文章深入學習 JavaScript系列 第 9

Day 09 [其他01] 7分鐘理解JS的節流、防抖及使用場景

  • 分享至 

  • xImage
  •  

文章選自

作者:薄荷前端

連接:https://juejin.im/post/6844903669389885453

來源:掘金

啥都沒有

<div class="box">
    <label for="none">
        啥都沒有: <input type="text" id="none">
    </label>
</div>
// 每次keyup及調用foo
function foo(content) {
  console.log('調用: ' + content)
}

let inputNone = document.getElementById('none')

inputNone.addEventListener('keyup', function (e) {
  foo(e.target.value)
})

動畫網址:
https://github.com/s95050937/IT30/blob/master/%E5%9C%96%E7%89%87%E5%8D%80/D09/01.gif

可以發現非常浪費資源而且消耗性能

函數防抖(debounce)

在事件被觸發n秒後再執行回調,如果n秒內又被觸發則重新計時

例子

<div class="box">
    <label for="debounce">
        防抖後: <input type="text" id="debounce">
    </label>
</div>
// 函數本體
function foo(content) {
  console.log('調用: ' + content)
}

function debounce(fun, delay) {
    return function (args) {
        let that = this
        let _args = args
        // 每次執行的時候重置setTimeout
        clearTimeout(fun.time)
        fun.time = setTimeout(function () {
            // 執行傳入的fun1(透過call方法傳遞參數_args)
            fun.call(that, _args)
        }, delay)
    }
}
    
let inputDebounce = document.getElementById('debounce')

let debounceFoo = debounce(foo, 500)

inputDebounce.addEventListener('keyup', function (e) {
    debounceFoo(e.target.value)
})

動畫網址:
https://github.com/s95050937/IT30/blob/master/%E5%9C%96%E7%89%87%E5%8D%80/D09/02.gif

函数節流 (throttle)

例子

<div class="box">
    <label for="throttle">
        節流後: <input type="text" id="throttle">
    </label>
</div>
function throttle(fun, delay) {
  let last, deferTimer
  return function (args) {
    let that = this
    let _args = args
    let now = +new Date()
    // 1. 如果last不存在代表第一次執行函數,所以可以直接執行函數
    // 2. 如果now小於 last + delay表示上一次執行函數的時間已經超過delay的時間
    //    所以可以直接執行函數(else代碼塊)
    if (last && now < last + delay) {
      // 底下確保不會發生
      // 最後執行代碼的時候,因為刷新last,造成不會執行最後的結果
      clearTimeout(deferTimer)
      deferTimer = setTimeout(function () {
        last = now
        fun.call(that, _args)
      }, delay)
    
    } else {
      last = now
      fun.call(that, _args)
    }
  }
}

let throttleAjax = throttle(ajax, 1000)

let inputc = document.getElementById('throttle')
inputc.addEventListener('keyup', function(e) {
    throttleAjax(e.target.value)
})

動畫網址:
https://github.com/s95050937/IT30/blob/master/%E5%9C%96%E7%89%87%E5%8D%80/D09/03.gif

補充

可能有人會對以下代碼不知道為啥,其實直接把他註解掉就很明顯

if (last && now < last + delay) {
    // 底下確保不會發生
    // 最後執行代碼的時候,因為刷新last,造成不會執行最後的結果
    clearTimeout(deferTimer)
    deferTimer = setTimeout(function () {
        last = now
        fun.call(that, _args)
    }, delay)

}

新的:

function throttle(fun, delay) {
  let last, deferTimer
  return function (args) {
    let that = this
    let _args = args
    let now = +new Date()
    // 裡面被註解
    if (last && now < last + delay) {
      // clearTimeout(deferTimer)
      // deferTimer = setTimeout(function () {
      //   last = now
      //   fun.call(that, _args)
      // }, delay)
    
    } else {
      last = now
      fun.call(that, _args)
    }
  }
}

let throttleFoo = throttle(ajax, 1000)

let inputThrottle = document.getElementById('throttle')
inputThrottle.addEventListener('keyup', function (e) {
  throttleFoo(e.target.value)
})

動畫網址:
https://github.com/s95050937/IT30/blob/master/%E5%9C%96%E7%89%87%E5%8D%80/D09/04.gif

有沒有注意到一個最關鍵的點,雖然最後並不會執行打印 aaaaaaaaaaa

因為打印完aaaaaaaa,我們的時長還不到一秒因此進入到 if 迴圈裡面啥都沒做

if (last && now < last + delay) {
 // clearTimeout(deferTimer)
 // deferTimer = setTimeout(function () {
 //   last = now
 //   fun.call(that, _args)
 // }, delay)
}

上一篇
Day 08 [原型鍊04] JavaScript常用八種繼承方案
下一篇
Day 10 [其他02] [譯] 理解JavaScript中的執行上下文和執行棧
系列文
從技術文章深入學習 JavaScript29
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言