Open1

debounce/throttleのメンタルモデル

convers39convers39
function debounce(Fn, wait) {
  let timer
  return function(...args) {
    clearTimeout(timer)
    timer = setTimeout(() => {
      Fn.apply(this, args)
    }, wait)
  }
}

function throttle(Fn, wait) {
  let shouldBlock = false
  return function(...args) {
    if (shouldBlock) return
    shoudBlock = true
    setTimeout(() => {
      shoudBlock = false
    }, wait)
    Fn.apply(this, args)
  }
}

  • timer変数はリターン関数のスコープではなく、debounceのスコープに置くことで、時系列の中で複数回呼び出しても、同じ変数へのアクセスが可能に(クロージャー)
  • 初回のclearTimeoutはどうでも良いが、2回目からは1回目のsetTimeoutで定義されたタイマーをクリア
  • 定義されたwaitが切れるまで無限ループ→切れなければクリアされる
  • throttleもクロージャーを利用したほぼ同じ考え方で、shouldBlockの値を定義された時間でtrue/falseを操作し、時系列の中で変更を達成する。

実装上のトラップ:

  • リターン関数はアロー関数書かない→thisundefinedになる
  • 逆にsetTimeoutにわたす関数はアロー関数で書く→thisがないので親スコープのリターン関数のthisを継承、これは意図通り
    • もしくはlet context = thisとかでcontextを渡す
    • もしくはbindで明示的バンドする、ただし読みにくくなるかも