函數(shù)防抖和節(jié)流

本文摘自這里扯键,通俗易懂睦袖。防抖和節(jié)流在前端和客戶(hù)端開(kāi)發(fā)中經(jīng)常會(huì)用到,而且適用場(chǎng)景還挺多荣刑,大多數(shù)成熟的第三方庫(kù)都有提供這些功能馅笙。對(duì)于初學(xué)者來(lái)書(shū),下面可以從概念和代碼嘶摊,更清晰簡(jiǎn)單得認(rèn)識(shí)他們延蟹。

在前端開(kāi)發(fā)的過(guò)程中,我們經(jīng)常會(huì)需要綁定一些持續(xù)觸發(fā)的事件叶堆,如 resize阱飘、scroll、mousemove 等等,但有些時(shí)候我們并不希望在事件持續(xù)觸發(fā)的過(guò)程中那么頻繁地去執(zhí)行函數(shù)沥匈。

通常這種情況下我們?cè)趺慈ソ鉀Q的呢蔗喂?一般來(lái)講,防抖和節(jié)流是比較好的解決方案高帖。

讓我們先來(lái)看看在事件持續(xù)觸發(fā)的過(guò)程中頻繁執(zhí)行函數(shù)是怎樣的一種情況缰儿。

html 文件中代碼如下

<div id="content" style="height:150px;line-height:150px;text-align:center; color: #fff;background-color:#ccc;font-size:80px;"></div>
<script>
    var num = 1;
    var content = document.getElementById('content');

    function count() {
        content.innerHTML = num++;
    };

    content.onmousemove = count;
</script>

在上述代碼中,div 元素綁定了 mousemove 事件散址,當(dāng)鼠標(biāo)在 div(灰色)區(qū)域中移動(dòng)的時(shí)候會(huì)持續(xù)地去觸發(fā)該事件導(dǎo)致頻繁執(zhí)行函數(shù)乖阵。效果如下


圖1

可以看到,在沒(méi)有通過(guò)其它操作的情況下预麸,函數(shù)被頻繁地執(zhí)行導(dǎo)致頁(yè)面上數(shù)據(jù)變化特別快瞪浸。所以,接下來(lái)讓我們來(lái)看看防抖和節(jié)流是如何去解決這個(gè)問(wèn)題的吏祸。

防抖(debounce)

所謂防抖对蒲,就是指觸發(fā)事件后在 n 秒內(nèi)函數(shù)只能執(zhí)行一次,如果在 n 秒內(nèi)又觸發(fā)了事件贡翘,則會(huì)重新計(jì)算函數(shù)執(zhí)行時(shí)間蹈矮。
防抖函數(shù)分為非立即執(zhí)行版和立即執(zhí)行版。

非立即執(zhí)行版:
function debounce(func, wait) {
    var timeout;

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

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

非立即執(zhí)行版的意思是觸發(fā)事件后函數(shù)不會(huì)立即執(zhí)行鸣驱,而是在 n 秒后執(zhí)行泛鸟,如果在 n 秒內(nèi)又觸發(fā)了事件,則會(huì)重新計(jì)算函數(shù)執(zhí)行時(shí)間丐巫。

我們依舊使用上述綁定 mousemove 事件的例子谈况,通過(guò)上面的防抖函數(shù),我們可以這么使用

content.onmousemove = debounce(count,1000);

效果如下


圖2

可以看到递胧,在觸發(fā)事件后函數(shù) 1 秒后才執(zhí)行碑韵,而如果我在觸發(fā)事件后的 1 秒內(nèi)又觸發(fā)了事件,則會(huì)重新計(jì)算函數(shù)執(zhí)行時(shí)間缎脾。

上述防抖函數(shù)的代碼還需要注意的是 this 和 參數(shù)的傳遞

var context = this;
var args = arguments;

防抖函數(shù)的代碼使用這兩行代碼來(lái)獲取 this 和 參數(shù)祝闻,是為了讓 debounce 函數(shù)最終返回的函數(shù) this 指向不變以及依舊能接受到 e 參數(shù)。

立即執(zhí)行版:
function debounce(func,wait) {
    var timeout;

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

        if (timeout) clearTimeout(timeout);

        var callNow = !timeout;
        timeout = setTimeout(function(){
            timeout = null;
        }, wait)

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

立即執(zhí)行版的意思是觸發(fā)事件后函數(shù)會(huì)立即執(zhí)行遗菠,然后 n 秒內(nèi)不觸發(fā)事件才能繼續(xù)執(zhí)行函數(shù)的效果联喘。

使用方法同上,效果如下

圖3

雙劍合璧版:

/**
 * @desc 函數(shù)防抖
 * @param func 函數(shù)
 * @param wait 延遲執(zhí)行毫秒數(shù)
 * @param immediate true 表立即執(zhí)行辙纬,false 表非立即執(zhí)行
 */
function debounce(func,wait,immediate) {
    var timeout;

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

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

節(jié)流(throttle)

所謂節(jié)流豁遭,就是指連續(xù)觸發(fā)事件但是在 n 秒中只執(zhí)行一次函數(shù)。節(jié)流會(huì)稀釋函數(shù)的執(zhí)行頻率贺拣。

對(duì)于節(jié)流蓖谢,一般有兩種方式可以實(shí)現(xiàn)捂蕴,分別是時(shí)間戳版和定時(shí)器版。

時(shí)間戳版:
function throttle(func, wait) {
    var previous = 0;

    return function() {
        var now = Date.now();
        var context = this;
        var args = arguments;
        if (now - previous > wait) {
            func.apply(context, args);
            previous = now;
        }
    }
}

使用方式如下

content.onmousemove = throttle(count,1000);

效果如下


圖3

可以看到闪幽,在持續(xù)觸發(fā)事件的過(guò)程中啥辨,函數(shù)會(huì)立即執(zhí)行,并且每 1s 執(zhí)行一次盯腌。

定時(shí)器版
function throttle(func, wait) {
    var timeout;

    return function() {
        var context = this;
        var args = arguments;
        if (!timeout) {
            timeout = setTimeout(function(){
                timeout = null;
                func.apply(context, args)
            }, wait)
        }

    }
}

使用方式同上溉知,效果如下


圖4

可以看到,在持續(xù)觸發(fā)事件的過(guò)程中腕够,函數(shù)不會(huì)立即執(zhí)行级乍,并且每 1s 執(zhí)行一次,在停止觸發(fā)事件后燕少,函數(shù)還會(huì)再執(zhí)行一次卡者。

我們應(yīng)該可以很容易的發(fā)現(xiàn),其實(shí)時(shí)間戳版和定時(shí)器版的節(jié)流函數(shù)的區(qū)別就是客们,時(shí)間戳版的函數(shù)觸發(fā)是在時(shí)間段內(nèi)開(kāi)始的時(shí)候,而定時(shí)器版的函數(shù)觸發(fā)是在時(shí)間段內(nèi)結(jié)束的時(shí)候材诽。

同樣地底挫,我們也可以將時(shí)間戳版和定時(shí)器版的節(jié)流函數(shù)結(jié)合起來(lái),實(shí)現(xiàn)雙劍合璧版的節(jié)流函數(shù)脸侥。

雙劍合璧版:

/**
 * @desc 函數(shù)節(jié)流
 * @param func 函數(shù)
 * @param wait 延遲執(zhí)行毫秒數(shù)
 * @param type 1 表時(shí)間戳版建邓,2 表定時(shí)器版
 */
function throttle(func, wait ,type) {
    if(type===1){
        var previous = 0;
    }else if(type===2){
        var timeout;
    }

    return function() {
        var context = this;
        var args = arguments;
        if(type===1){
            var now = Date.now();

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

    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市睁枕,隨后出現(xiàn)的幾起案子官边,更是在濱河造成了極大的恐慌,老刑警劉巖外遇,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件注簿,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡跳仿,警方通過(guò)查閱死者的電腦和手機(jī)诡渴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)菲语,“玉大人妄辩,你說(shuō)我怎么就攤上這事∩缴希” “怎么了眼耀?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)佩憾。 經(jīng)常有香客問(wèn)我哮伟,道長(zhǎng)干花,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任澈吨,我火速辦了婚禮把敢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘谅辣。我一直安慰自己修赞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布桑阶。 她就那樣靜靜地躺著柏副,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蚣录。 梳的紋絲不亂的頭發(fā)上割择,一...
    開(kāi)封第一講書(shū)人閱讀 52,394評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音萎河,去河邊找鬼荔泳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛虐杯,可吹牛的內(nèi)容都是我干的玛歌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼擎椰,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼支子!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起达舒,我...
    開(kāi)封第一講書(shū)人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤值朋,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后巩搏,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體昨登,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年塔猾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了篙骡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡丈甸,死狀恐怖糯俗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情睦擂,我是刑警寧澤得湘,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站顿仇,受9級(jí)特大地震影響淘正,放射性物質(zhì)發(fā)生泄漏摆马。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一鸿吆、第九天 我趴在偏房一處隱蔽的房頂上張望囤采。 院中可真熱鬧,春花似錦惩淳、人聲如沸蕉毯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)代虾。三九已至,卻和暖如春激蹲,著一層夾襖步出監(jiān)牢的瞬間棉磨,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工学辱, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留乘瓤,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓策泣,卻偏偏與公主長(zhǎng)得像馅扣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子着降,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 在前端開(kāi)發(fā)的過(guò)程中,我們經(jīng)常會(huì)需要綁定一些持續(xù)觸發(fā)的事件拗军,如 resize任洞、scroll、mousemove 等等...
    Grace_feb3閱讀 396評(píng)論 0 0
  • 在前端開(kāi)發(fā)的過(guò)程中发侵,我們經(jīng)常會(huì)需要綁定一些持續(xù)觸發(fā)的事件交掏,如 resize、scroll刃鳄、mousemove 等等...
    淘淘笙悅閱讀 225,842評(píng)論 42 349
  • 在前端開(kāi)發(fā)的過(guò)程中盅弛,我們經(jīng)常會(huì)需要綁定一些持續(xù)觸發(fā)的事件,如 resize叔锐、scroll挪鹏、mousemove 等等...
    meng_281e閱讀 194評(píng)論 1 0
  • 轉(zhuǎn)自 簡(jiǎn)書(shū) 什么是函數(shù)防抖和節(jié)流 在前端開(kāi)發(fā)過(guò)程中,我們經(jīng)常會(huì)遇到需要綁定一些持續(xù)性出發(fā)事件的場(chǎng)景.例如resiz...
    TouchMe丶閱讀 397評(píng)論 1 0
  • 定義 在JS里愉烙,有一些事件是很容易頻繁觸發(fā)的讨盒,比如窗口的resize、scroll步责、鼠標(biāo)的onmousemove等...
    Rudy_Ran閱讀 181評(píng)論 0 2