飽きっぽいテーマ--節流防震実現

4746 ワード

80%のフロントエンドの面接でこの問題に遭遇したことがあるかもしれませんが、無数の人がこの方面の文章を書いたことがありますが、私はまた書きたいと思っています.記録するためだけでなく、自分の考えを表現したいと思っています.

概念の解釈


まず節流防震の概念についてお話ししましょう
  • スロットル:はっきり言って、時間間隔をカスタマイズして、この方法をこの時間ごとに実行させて、他のトリガのこの方法はすべて無視します.最も一般的なscrollのような操作は、実行頻度がそんなに高くなく、性能に非常に影響を与える必要はありません.数百ミリ秒で1回実行すれば十分です.もちろん、具体的な実行時間間隔はビジネスのニーズに応じて
  • になります.
  • ブレ止め:はっきり言って、あなたがまだ操作しているので、私はそれを実行しません.あなたが止まってから、私は実行します.やはりスクロールscrollについて言えば、ドキュメントのscrollを傍受していますが、スクロール中に定義された方法を実行しないで、止まってから実行したいと思っています.それは典型的なブレ止め
  • です.
    始める前に、setTimeoutの戻り値をご紹介します. timeoutID 、タイマーの番号を表します.この値は、clearTimeout()に渡されて、タイマをキャンセルすることができる.setTimeout()およびsetInterval(``) , , cleartimeout() clearInterval``は交換可能であることに注意されたい.ただし、混同を避けるためにキャンセルタイミング関数を混用しない
    以下詳しく述べる

    しぼり


    一般的に、スロットルには次のような方法があります.
  • の第1種は閉パケットを利用して、最終的に関数を返して、まず1つの時間フラグビットpreviousを定義して、この値は毎回実行した後の時間(初回実行previousは実行時時間)で、 previousの差は定義の間隔時間より大きいならば実行して、さもなくば直接returnは
  • を落とします.
    具体的なコードを見てください
    一般版
    function throttle(fn, wait=200) {
        const self = this //  this,          
       let previous = Date.now() //        
        return function() { //     ,         throttle        ,      self  previous  
               let now = Date.now() //     
            if(now - previous > wait) { //      previous           ,   
                previous = now 
                fn.apply(self, Array.prototype.slice.call(arguments)) //  ,    ,  arguments    ,         
            }else{
                return
             }
        }
    }
    

    しかし、トリガの場合、設定待ち時間後に初めて実行される場合がある.最初にトリガーして実行したい場合は、後の間隔設定の時間で実行しますが、どうすればいいですか?最初からpreviousを0にするだけで、0かどうかを判断するたびに実行します.これでいいのではないでしょうか.コードは以下の通りです.
    先行実行版
    function throttle(fn, wait=200) {
        const self = this 
       let previous = 0 //      0
        return function() { 
              const args = Array.prototype.slice.call(arguments)
              if(!previous) {
                fn.apply(self, args) //previous 0     
                previous = Date.now() //       previous,         previous             ,    0  
                return
              }
               let now = Date.now() //     
            if(now - previous > wait) {
                previous = now 
                fn.apply(self, args)
            }else{
                return
             }
        }
    }
    
  • の2つ目はsettimeoutを利用して、timerメモリタイマidを設定し、最初はtimerがundefinedであり、実行前にtimerをnullに設定して、トリガのたびに現在実行すべきコールバックがあるかどうかを判断し、
  • を実行し続けることである.
    コードを見て
    settimeoutの一般版の利用
    function throttle(fn, wait=200) {
        const self = this //  this,          
        let timer //     ID
        return function() {
            const args = Array.prototype.slice.call(arguments) //  
            if(!timer) { //              
               timer = setTimeout(function() {
                    timer = null //      ,     ,  timer  ,                
                    fn.apply(self, args) //     
                }, wait)
            }
        }    
    }
    

    通常版と同じように、最初にトリガーして実行するには、判断するだけです.
    function throttle(fn, wait=200) {
            ... 
            if(!timer) {
               fn.apply(self, args) //      
               timer = setTimeout(function() {
              ...
        }    
    }
    

    ブレ止め


    スロットルを理解すれば、ブレ防止も比較的わかりやすいです.簡単に言えば、アーカイブされた間隔で関数を実行すると、タイミングが再起動されます.

    通常版

    function debounce(fun, delay) {
        let timer;
        return function (args) {
            let self = this
            if(timer) { //     ,   
              clearTimeout(timer)
            }
            timer = setTimeout(function () {
                fun.apply(self, Array.prototype.slice.call(arguments))
            }, delay)
        }
    }
    

    同様に、以上のコードは、連続トリガのみが、トリガを停止して設定を待つ時間後にコールバックが実行される場合もある.最初にトリガして実行したい場合は、後続の連続トリガは実行されず、停止後に設定される時間を待ってからコールバックが実行されます.
    実は私は最初にタイマidが存在するかどうかを追加するだけで、これが最初のトリガであるかどうかを判断することができます.

    プリトリガ版


    次のようになります.
    function debounce(fun, delay) {
        let timer;
        let self = this
        return function (args) {
            const args = Array.prototype.slice.call(arguments)
            !timer && fun.apply(self, args) //     ,
            if(timer) { //     ,   
              clearTimeout(timer)
            }
            timer = setTimeout(function () {
                fun.apply(self,args)
            }, delay)
        }
    }
    

    以上のコードはみんなcopyの下で検証することができて、throttleをテストして下の対応する関数名を交換することを覚えています
    function con() {
        console.log(222)
    }
    document.addEventListener('scroll', debounce(con, 200))
    

    まとめ


    コードが少し多いかもしれませんが、節流と振れ防止の目的を覚えておけば、実現方法はいくつかあります.例えば、最初のトリガがすぐに実行されるかどうか、最後のトリガが必ず実行されるかどうか、節流と振れ防止の監視を解除するように構成することもできます.方法は多種多様で、基本的なことを理解して、その他はどのようにするのはまだ手のひらを返すように簡単ではありませんて、いいでしょう聡明な仲間たち