ブレ止めとスロットルを簡単に理解

5025 ワード

ジッタ防止とスロットルはフロントエンドで非常に多く、検索ボックス、クエリーなどの操作、無効な操作をどのように減らし、サーバーの圧力を軽減するか、例えば持続的に発生するイベント、resize、scroll、mousemoveなどがありますが、頻繁に関数を実行したくないので、性能に影響を与えるのは非常に有用なテクニックです.
トリガし続けるシーン
次はmousemoveを傍受するコードで、マウスが移動すると数字が変化していることがわかります.
let num = 1; let content = document.getElementById('content'); function count() { content.innerHTML = num++; }; content.onmousemove = count;

在上述代码中,div 元素绑定了 mousemove 事件,当鼠标在 div(灰色)区域中移动的时候会持续地去触发该事件导致频繁执行函数。

可以看到,在没有通过其它操作的情况下,函数被频繁地执行导致页面上数据变化特别快。所以,接下来让我们来看看防抖和节流是如何去解决这个问题的。

防抖(debounce)

所谓防抖,就是指触发事件后在 n 秒内函数只能执行一次,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。
防抖函数分为非立即执行版和立即执行版。

非立即执行版:

function debounce(func, wait) {
    let timeout;
    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout);
        
        timeout = setTimeout(() => {
            func.apply(context, args)
        }, wait);
    }
}

即時実行版ではなく、イベントがトリガーされた後に関数が直ちに実行されず、n秒後に実行され、n秒以内にイベントがトリガーされた場合、関数の実行時間が再計算されます.
上記のmousemoveイベントをバインドする例を使用して、上記の手ぶれ防止関数を使用して、このように使用することができます.
content.onmousemove = debounce(count,1000);

イベントがトリガーされてから関数は1秒後に実行され、イベントがトリガーされてから1秒以内にイベントがトリガーされた場合、関数の実行時間が再計算されることがわかります.
上記のジッタ防止関数のコードはthisとパラメータの伝達に注意しなければならない.
let context = this;
let args = arguments;

ジッタ防止関数のコードこの2行のコードを使用してthisとパラメータを取得するのは、debounce関数が最終的に返される関数thisが変わらず、eパラメータが受け入れられるようにするためです.
今すぐ実行:
function debounce(func,wait) {
    let timeout;
    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout);

        let callNow = !timeout;
        timeout = setTimeout(() => {
            timeout = null;
        }, wait)

        if (callNow) func.apply(context, args)
    }
}

即時実行版とは、イベントがトリガーされると関数がすぐに実行され、n秒以内にイベントがトリガーされないと関数の効果が実行されないことを意味します.
使用方法と同様に、開発の過程で、どのバージョンのジッタ防止関数を使用する必要があるかを異なるシーンに基づいて決定する必要があります.一般的に、上記のジッタ防止関数はほとんどのシーンのニーズを満たすことができます.しかし、非即時実行版と即時実行版のジッタ防止関数を組み合わせて、最終的な双剣合璧版のジッタ防止関数を実現することもできます.
双剣合璧版:
/**
 * @desc     
 * @param func   
 * @param wait        
 * @param immediate true      ,false       
 */
function debounce(func,wait,immediate) {
    let timeout;

    return function () {
        let context = this;
        let args = arguments;

        if (timeout) clearTimeout(timeout);
        if (immediate) {
            var callNow = !timeout;
            timeout = setTimeout(() => {
                timeout = null;
            }, wait)
            if (callNow) func.apply(context, args)
        }
        else {
            timeout = setTimeout(function(){
                func.apply(context, args)
            }, wait);
        }
    }
}

スロットル
スロットルとは,連続トリガイベントを指すが,n秒で1回の関数しか実行しない.スロットルは関数の実行頻度を希釈します.
スロットルの場合、タイムスタンプ版とタイマ版の2つの方法が一般的に実現される.
タイムスタンプ:
function throttle(func, wait) {
    let previous = 0;
    return function() {
        let now = Date.now();
        let context = this;
        let args = arguments;
        if (now - previous > wait) {
            func.apply(context, args);
            previous = now;
        }
    }
}

使用方法は以下の通りです.
content.onmousemove = throttle(count,1000);

イベントが継続的にトリガーされると、関数はすぐに実行され、1 sごとに実行されることがわかります.
タイマー版:
function throttle(func, wait) {
    let timeout;
    return function() {
        let context = this;
        let args = arguments;
        if (!timeout) {
            timeout = setTimeout(() => {
                timeout = null;
                func.apply(context, args)
            }, wait)
        }
    }
}

同様に、イベントをトリガし続ける間、関数はすぐに実行されず、1 sごとに実行され、イベントをトリガした後、関数は再び実行されることがわかります.
タイムスタンプ版とタイマ版のスロットル関数の違いは、タイムスタンプ版の関数トリガが時間帯内で開始されたとき、タイマ版の関数トリガが時間帯内で終了したときであることを容易に発見できるはずです.
同様に,タイムスタンプ版とタイマ版のスロットル関数を組み合わせて,両剣合璧版のスロットル関数を実現することもできる.
双剣合璧版:
/**
 * @desc     
 * @param func   
 * @param wait        
 * @param type 1      ,2      
 */
function throttle(func, wait ,type) {
    if(type===1){
        let previous = 0;
    }else if(type===2){
        let timeout;
    }
    return function() {
        let context = this;
        let args = arguments;
        if(type===1){
            let now = Date.now();

            if (now - previous > wait) {
                func.apply(context, args);
                previous = now;
            }
        }else if(type===2){
            if (!timeout) {
                timeout = setTimeout(() => {
                    timeout = null;
                    func.apply(context, args)
                }, wait)
            }
        }
    }
}