JS関数の手ぶれと節流を詳しく分析します。


本稿の内容は節流と振動除去の概念基礎知識から、JS関数に対して詳細な分析を行いました。
1、流れと震えは何ですか?
流れを増す。蛇口をしっかり締めて水を少し少なめに流しますが、水が流れないようにします。現実の生活の中で時々水を汲む必要があると想像してください。水を汲むと同時にずっとそこに立って待っていたくないです。後で他のことをしてください。水を一桶いっぱい流した時に戻ってきてください。この時は蛇口を大きく開けてはいけません。帰って来ないと水がいっぱいになります。水をたくさん浪費します。この時は節流が必要です。自分を帰らせる時は水がほぼ満杯です。JSにはこのような状況がありますか?典型的なシーンは怠惰にページを監視するscollイベントをロードしたり、マウスのmousemooveイベントを傍聴したりします。これらの事件に対応する処理方法は水に相当します。scrollとmousemoemooveはマウスの移動時にブラウザの周波数に触発され、対応するイベントも頻繁に触発されます。このようにして、ブラウザのリソースのオーバーヘッドが大きくなります。そして、多くの中間処理が必要ではないです。このようにブラウザのキャトン現象を引き起こします。この時は節流が必要です。どうやって節流しますか?ブラウザが対応するイベントをトリガしないようにすることはできませんが、イベントを処理する方法の実行頻度を減らすことができます。
震えに行く。最初にこの言葉に触れたのは高校の物理で習ったはずです。スイッチは本当に閉じる前にぶれが発生することがあります。もし振動が明らかであれば、対応する小さい電球が点滅して、電球を点滅させることは重要ではありません。もし目をもう一度点滅させたら面倒です。私達のページにもこのような状況があります。もし私達の入力ボックスが入力されたら、内容を入力しながら楽屋に対応する連想語を調べに行きます。ユーザーが入力すると同時に、頻繁にinputイベントをトリガし、その後頻繁に後ろに向かって送ってください。バックグラウンドからのデータの戻りが遅いと、表示されている連想語が頻繁に変換され、最後の要求が戻ってきます。この場合、再度入力したかどうかは一定の時間でモニターできます。再入力していないと、今回の入力が完了したと判断し、要求を送信します。そうでなければ、ユーザがまだ入力していると判定し、要求を送信しません。
デフとスロットルは違っています。節流は中間の処理関数が制限されていますが、周波数を減らすだけで、デフは中間の処理関数を全部フィルタして、規定判定時間内の最後のイベントだけを実行します。
2、JS実現。
前のBBはこんなに多くなりました。ここを見てくれてありがとうございます。これからは自分で手を動かしてみます。節流と震えを実現する方法を見てみます。
スロットル:

/**     :
  **            ,          ,
  **       lastTime             
  **/
  function throttle (func, wait) {
   let lastTime = null    //         lastTime    ,  js       function            
   return function () {
    let now = new Date()
    //                          ,   
    if (now - lastTime - wait > 0) {
     func()
     lastTime = now
    }
   }
  }
呼び出しはどうなりますか?

//        ,      
let throttleRun = throttle(() => {
  console.log(123)
}, 400)
window.addEventListener('scroll', throttleRun)
この時、f狂ったスクロールページは、四百msで123を印刷します。節流がないとどんどん印刷します。waitパラメータを変えて、違った感じを受けることができます。
しかし、ここでは、私たちの節流方法は完全ではありません。イベント発生時のthisオブジェクトを取得する方法はありません。また、私たちの方法は簡単で乱暴なので、今回のトリガの時間と前回の実行時間の間隔を判断して、フィードバックを実行するかどうかを決定します。これで最後のトリガが実行できなくなります。実行できず、誤殺されたので、方法を改善する必要があります。

function throttle (func, wait) {
   let lastTime = null
   let timeout
   return function () {
    let context = this
    let now = new Date()
    //                          ,   
    if (now - lastTime - wait > 0) {
     //              
     if (timeout) {
      clearTimeout(timeout)
      timeout = null
     }
     func.apply(context, arguments)
     lastTime = now
    } else if (!timeout) {
     timeout = setTimeout(() => {
      //          
      func.apply(context, arguments)
     }, wait)
    }
   }
  }
これにより、私達の方法は比較的完全になりました。呼び出し方法は前と同じです。
震えに行く:
手振れの取り方は、節流と考え方が一致しますが、手ぶれが判定された後だけ、方法が実行されます。

debounce (func, wait) {
   let lastTime = null
   let timeout
   return function () {
    let context = this
    let now = new Date()
    //         
    if (now - lastTime - wait > 0) {
     setTimeout(() => {
      func.apply(context, arguments)
     }, wait)
    } else {
     if (timeout) {
      clearTimeout(timeout)
      timeout = null
     }
     timeout = setTimeout(() => {
      func.apply(context, arguments)
     }, wait)
    }
    //     lastTime        
    lastTime = now
   }
  }
この時、前と同じように呼び出したら、どんなに狂ったようにウィンドウをスクロールしても、スクロールを停止した時だけ、対応するイベントが実行されます。
手ぶれや節流はすでに多くの成熟したjsが実現しており、その大まかな考え方は基本的にこのようである。
私達は更にみんなにネット友達の実現方法のコードを共有します。
方法1
1.このような実施形態の考えはよく理解されています。例えば50ミリ秒の間隔を設定し、この時間を基準にタイマを設定し、第1のトリガイベントから第2のトリガイベントまでの間隔が50ミリ秒未満の場合、このタイマーをクリアし、新たなタイマーを設定し、イベントが発生してから50ミリ秒以内に繰り返しトリガしないようにします。コードは以下の通りです

function debounce(method){ 
 clearTimeout(method.timer); 
 method.timer=setTimeout(function(){ 
  method(); 
 },50); 
} 
このような設計方法には問題があります。何度も触発すべきイベントは最終的に一回しか発生しないかもしれません。具体的には、順を追って漸進的にスクロールされるイベントは、ユーザのスクロールが速すぎるか、またはプログラム設定の関数のスロットル間隔が長すぎると、最終的にスクロールイベントは突然のジャンプイベントとして現れ、中間プロセスはすべてスロットルカットされてしまう。この例ではちょっと誇張していますが、このような方式で節流を行うと、最終的にはプログラムが節流しない時よりも「もっと突飛」であることが明らかになります。このような欠陥を補う設計的な考え方がある。
方法2
2.第2の実施形態の考え方は、第1の実施形態とは少し違っています。50ミリ秒などの間隔を設定し、この時間を基準として、イベントを安定的に分離するトリガが発生した場合、つまり、100ミリ秒以内に連続して複数のイベントをトリガし、50ミリ秒で一回だけ安定的に分離して実行します。コードは以下の通りです

var oldTime=new Date().getTime(); 
var delay=50; 
function throttle1(method){ 
 var curTime=new Date().getTime(); 
 if(curTime-oldTime>=delay){ 
  oldTime=curTime; 
  method(); 
 } 
} 
第一の方法に比べて、第二の方法は第一の方法よりも何度も実行されるかもしれないが、第一の方法は中間プロセスの欠陥を除去するためによく解決される。そのため、具体的なシーンでは、状況に応じてどのような方法を選ぶべきかを決めます。
方法二について、もう一つ同じ機能の書き方を提供します。

var timer=undefined,delay=50; 
function throttle2(method){ 
 if(timer){ 
  return ; 
 } 
 method(); 
 timer=setTimeout(function(){ 
  timer=undefined; 
 },delay); 
}