js 防抖和節(jié)流

防抖

防抖是js優(yōu)化的重要的一部分,也是面試中手寫代碼最??嫉念}目。那么我們?yōu)槭裁匆蓝??防抖是什么意思?br> 比如我們在監(jiān)聽onkeyup事件中,監(jiān)聽input中輸入的文字,我們可以在console中可以看到input的文字打印。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <input id="input1" type="text">
</body>
<script src="index.js"></script>
</html>
const input1 = document.getElementById('input1');
// console.log(input1);
input1.addEventListener('keyup', function() {
    console.log(input1.value);
})

console打印

非常簡單。但是每打一個字就監(jiān)聽到了,就很耗性能。
有人就問了,誒?那我能不能等我打完一小部分字段了以后,然后瀏覽器才去監(jiān)聽呢?
又有人說了,用setTimeout不就完了,過個一秒再監(jiān)聽。
所以,防抖這個詞就來了。

所以我們在剛才的index.js中的代碼中,input1的監(jiān)聽中,加入一個定時器試試:

const input1 = document.getElementById('input1');
// console.log(input1);
let timer = null;
input1.addEventListener('keyup', function() {
    if(timer) {
        clearTimeout(timer);
    }
    timer = setTimeout(() => {
        console.log(input1.value);
        // 清空定時器
        timer = null;
    }, 500)
})

先演示一下:


加入防抖功能

可以看到,當我直接依次輸入123456的時候,停頓了一會,然后瀏覽器才監(jiān)聽到我輸入了,并打印123456,接著我又輸入了789停止,半秒后,又打印了123456789,很妙。就是說,你一直輸一直輸,只要你中間輸入的間斷不超過500毫秒(咱們自己定的時間),它就不會出來,就一直等著你輸,什么時候你輸入的間斷,暫停的或者停止的時間超過500毫秒,它才會把最終的結(jié)果打印出來。

用很通俗的話說一下這里面的原理:
①先弄個定時器,監(jiān)聽到keyup的時候看一下現(xiàn)在定時器存在嗎?不存在,是null,那我們不管,往下走。
②接下來給定時器賦值setTimeout的異步任務(wù),這個異步任務(wù)是500毫秒之后執(zhí)行,執(zhí)行的結(jié)果就是打印當前的input中的value。打印完之后,要清空定時器。

流程是醬的:
①輸入個1,停止:
timernull,if語句不觸發(fā),往下進行,500毫秒后輸出value,然后清空timer,這是第一次,而且只輸入一個1的情況下,比較簡單。
②輸入123,停止:
timernull,if語句不觸發(fā),往下進行,500毫秒之后執(zhí)行,timer有值了,這個時候,我們立馬就輸入2了,keyup又監(jiān)聽了,這時候500毫秒還沒有到,監(jiān)聽之后,timer是有值的,if條件判斷為true,因為之前輸入1的時候,timer就有值了,所以我們把之前輸入1的那個timer給清除掉,然后下面重新設(shè)置timer,500毫秒之后執(zhí)行,但是還沒開始執(zhí)行的時候,又輸入3了,立馬又會出現(xiàn)keyup事件,進入if,有timer,這個timer是輸入2的時候定義的,然后又清空了,重新設(shè)置了定時任務(wù),500毫秒只有執(zhí)行,這時候已經(jīng)沒有值進來了,所以500毫秒到了以后,輸出剛才的123。接下來如果再輸入一個4,是和第一次輸入一個1是一樣的。

寫的比較啰嗦,但是這就是傳說中的防抖。

但是有個問題,就是我們?yōu)榱艘粋€input1,就寫了這么多行代碼,感覺好麻煩啊,如果我們又10個input,我們是不是得寫10個這樣的監(jiān)聽函數(shù)?
所以新的問題就來了,我們需要改進一下我們的防抖函數(shù),將它簡單的封裝一下:

我們需要將防抖函數(shù)封裝成一個公共方法,那么這個方法的參數(shù)應(yīng)該有個函數(shù)fn,和一個delay時間延遲,然后最終要返回一個函數(shù)。
代碼如下:

// 防抖
function debounce(fn, delay = 500) {
    // timer 是在閉包中的
    let timer = null; 
    return function() {
        if(timer) {
            clearTimeout(timer);
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments);
            // 清空定時器
            timer = null;
        }, delay)
    }
}
input1.addEventListener('keyup', debounce(function() {
    console.log(input1.value);
}), 600);

?

節(jié)流

但我們拖拽一個元素時,要隨時拿到該元素被拖拽的位置,直接用drag事件,則會頻發(fā)觸發(fā),很容易導致卡頓。節(jié)流的意思是:無論拖拽的速度有多快,都會每隔100ms觸發(fā)一次。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        #div1 {
            border: 1px solid #ccc;
            width: 200px;
            height: 100px;
        }
    </style>
</head>
<body>
    <div id="div1" draggable="true">可拖拽</div>
</body>
<script src="index.js"></script>
</html>
const div1 = document.getElementById('div1');
// console.log(div1);
div1.addEventListener('drag', function(e) {
    console.log(e.offsetX, e.offsetY);
})

拖拽打印

可以看到,drag基本上隨時都在觸發(fā),這時候我們?nèi)绻僬{(diào)用別的js的功能,很容易造成負載驗證,卡死。
所以我們和防抖一樣,加個setTimeout

const div1 = document.getElementById('div1');
// console.log(div1);
let timer = null;
div1.addEventListener('drag', function(e) {
    if(timer) {
        return;
    }
    timer = setTimeout(() => {
        console.log(e.offsetX, e.offsetY);
        // 清空定時器
        timer = null;
    }, 100)
})

只不過這次如果不存在timer,我們直接return出去,就不管了,自生自滅,不響應(yīng)了。知道100毫秒之后,打印了,并且timer被賦值為null,然后才會有timer的下一次setTimeout...具體的流程就不說了,和防抖是一樣的。

同樣,我們需要將節(jié)流函數(shù)封裝一下,工具化:

// 節(jié)流
function throttle(fn, delay = 100) {
    let timer = null;
    return function() {
        if(timer) {
            return;
        }
        timer = setTimeout(() => {
            fn.apply(this, arguments);
            timer = null;
        }, delay)
    }
}

div1.addEventListener('drag', throttle(function(e) {
    console.log(e.offsetX, e.offsetY);
}), 200)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 使用window.addEventListener()進行窗口的resize、scroll、輸入框?qū)崟r監(jiān)控等操作時...
    DDLH閱讀 411評論 0 1
  • web開發(fā)中經(jīng)常會做滾動監(jiān)聽,比如商品分類功能:左右兩列,左側(cè)類目,右側(cè)商品,需要監(jiān)聽右測商品列表的滾動,滾到哪個...
    大海愛奔跑閱讀 194評論 0 1
  • 一、什么是防抖和節(jié)流 Ps: 比如搜索框,用戶在輸入的時候使用change事件去調(diào)用搜索,如果用戶每一次輸入都去搜...
    拾柒_aab0閱讀 311評論 0 1
  • 防抖: 觸發(fā)事件后,在 n 秒內(nèi)函數(shù)只能執(zhí)行一次,如果觸發(fā)事件后在 n 秒內(nèi)又觸發(fā)了事件,則會重新計算函數(shù)延執(zhí)行...
    _小飛飛閱讀 424評論 0 1
  • 在進行窗口的resize、scroll,輸入框內(nèi)容校驗等操作時,如果事件處理函數(shù)調(diào)用的頻率無限制,會加重瀏覽器的負...
    iqing2012閱讀 891評論 0 1

友情鏈接更多精彩內(nèi)容