手寫防抖函數(shù) debounce 和節(jié)流函數(shù) throttle

手寫防抖函數(shù) debounce 和節(jié)流函數(shù) throttle

本文參考:

基礎(chǔ)理論

最近看到這么一道面試題:手寫實(shí)現(xiàn) debounce 和 throttle妓肢。

盜圖祖灰,侵刪

一臉懵逼效览,真的是轻庆。這兩個(gè)英文單詞都是什么鬼(原諒我英文詞匯量太爛),后來看了下,原來是防抖和節(jié)流的意思啊。

那么,防抖和節(jié)流又是什么東西吱韭?

這兩個(gè)東西,其實(shí)都是用來處理某個(gè)工作短時(shí)間內(nèi)過于頻繁觸發(fā)的場(chǎng)景鱼的,只是根據(jù)不同的處理方式有不同的說法理盆。

防抖:某個(gè)函數(shù)在短時(shí)間內(nèi)只執(zhí)行最后一次。

意思也就是說凑阶,函數(shù)被觸發(fā)時(shí)猿规,需要先延遲,在延遲的時(shí)間內(nèi)宙橱,如果再次被觸發(fā)坎拐,則取消之前的延遲,重新開始延遲养匈。這樣就能達(dá)到哼勇,只響應(yīng)最后一次,其余的請(qǐng)求都過濾掉呕乎。

這種處理方式有很多實(shí)際的應(yīng)用場(chǎng)景:比如對(duì)輸入框數(shù)據(jù)的校驗(yàn)處理积担,沒必要每輸入一個(gè)字符就校驗(yàn)一遍;

節(jié)流:某個(gè)函數(shù)在指定時(shí)間段內(nèi)只執(zhí)行第一次猬仁,直到指定時(shí)間段結(jié)束帝璧,周而復(fù)始。

跟防抖不一樣的是湿刽,節(jié)流是指定時(shí)間段內(nèi)只執(zhí)行第一次的烁,也就是這段時(shí)間內(nèi),只需要響應(yīng)第一次的請(qǐng)求即可诈闺,后續(xù)的請(qǐng)求都會(huì)被過濾掉渴庆,直到下個(gè)時(shí)間段,重新來過雅镊,周而復(fù)始襟雷。

應(yīng)用場(chǎng)景:Android 里的屏幕刷新機(jī)制,每個(gè)幀(16.6ms)內(nèi)仁烹,不管進(jìn)行了多少次請(qǐng)求界面刷新的操作耸弄,只需響應(yīng)第一次的請(qǐng)求,去向底層注冊(cè)監(jiān)聽?zhēng)盘?hào)即可卓缰。因?yàn)榻邮盏綆盘?hào)后计呈,是通過遍歷 View 樹來刷新界面砰诵,所以注冊(cè)的動(dòng)作只需要進(jìn)行一次就夠了。Vue 的虛擬 DOM 的刷新也是類似的機(jī)制捌显。

以上這些概念還不足以明白的話胧砰,再看張圖(盜自開頭鏈接中的文章):

盜用侵刪

這樣一來就理解了吧,第一行表示不做任何處理苇瓣,頻繁調(diào)用函數(shù),每次都會(huì)響應(yīng)偿乖;

經(jīng)過 debounce 防抖處理后击罪,只響應(yīng)最后一次,因?yàn)榉蓝侗举|(zhì)上就是通過延遲贪薪,所以實(shí)際執(zhí)行函數(shù)時(shí)機(jī)會(huì)晚于函數(shù)的請(qǐng)求時(shí)機(jī)媳禁;

而經(jīng)過 throttle 節(jié)流處理后,是按一定的頻率來處理這堆頻繁調(diào)用的函數(shù)画切,每個(gè)周期內(nèi)竣稽,只響應(yīng)第一次,過濾后面的請(qǐng)求霍弹,直到下個(gè)周期毫别。

其實(shí),或許你并沒有接觸到 debounce 防抖或 throttle 節(jié)流這種專業(yè)術(shù)語(yǔ)的說法典格,但實(shí)際開發(fā)中岛宦,你肯定或多或少有進(jìn)行過類似防抖或節(jié)流的處理。下面講講它的實(shí)現(xiàn)耍缴,你就會(huì)發(fā)現(xiàn)砾肺,很似曾相識(shí)。

手寫 throttle 節(jié)流函數(shù)

節(jié)流防嗡,顧名思義变汪,就是節(jié)省流量。那么蚁趁,為什么可以節(jié)流裙盾,自然就是這頻繁被觸發(fā)的工作,其實(shí)沒必要次次響應(yīng)他嫡。

我們上面舉了個(gè) Android 的屏幕刷新機(jī)制的例子闷煤,也就是在一個(gè)周期內(nèi),可以有無數(shù)次會(huì)觸發(fā)屏幕刷新的操作涮瞻,但其實(shí)只要第一次的操作去注冊(cè)一下幀信號(hào)就可以了鲤拿。

實(shí)現(xiàn)上,其實(shí)也很簡(jiǎn)單署咽,就是加個(gè)標(biāo)志位而已:

function throttle(fn, interval = 200) {
    let flag = null;
    return function(...args) {
        if (!flag) {
            flag = true;
            setTimeout(() => {
                flag = false;
                fn.call(this, ...args);
            }, interval);
        }
    }
} 

是吧近顷,就是簡(jiǎn)單的加個(gè)標(biāo)志位來進(jìn)行過濾生音。這里有個(gè)關(guān)鍵的點(diǎn):fn.call(this, ...args),為什么要通過 call 這種修改函數(shù)內(nèi)部 this 的方式來調(diào)用原函數(shù)窒升?直接 fn() 不行嗎缀遍?

原因在手寫 debounce 里分析吧,因?yàn)槟抢镆彩且粯拥奶幚怼?/p>

那么饱须,看到這個(gè)實(shí)現(xiàn)方案域醇,有沒有感覺有點(diǎn)熟悉,在項(xiàng)目中肯定會(huì)有所接觸的蓉媳,雖然由于這里的 throttle 函數(shù)是個(gè)通用的工具函數(shù)譬挚,而且是高階函數(shù),可能在項(xiàng)目中看到的不多酪呻。至少减宣,我好像并沒有在實(shí)際項(xiàng)目中使用過。

但這樣的玩荠,你肯定經(jīng)常寫:

var flag = null;

function a() {
    if (!flag) {
        flag = true;
        // do something
        // 在某個(gè)回調(diào)里將 flag = false; 
    }
}

這種通過 flag 標(biāo)志位過濾重復(fù)事件的處理漆腌,其實(shí)就跟節(jié)流的思想有點(diǎn)類似。區(qū)別只是阶冈,節(jié)流是通過一定的頻率來修改標(biāo)志位闷尿,來重新放行,而上面這種用法女坑,則是依賴于某個(gè)任務(wù)完成后悠砚,再去回調(diào)修改標(biāo)志位,也就是任務(wù)不完成堂飞,重復(fù)的事件都會(huì)被過濾灌旧。但兩者的思想其實(shí)很類似。

手寫 debounce 防抖函數(shù)

防抖處理我實(shí)際中用得比較多绰筛,所以打算講講枢泰,網(wǎng)上大眾的實(shí)現(xiàn),以及我針對(duì)具體項(xiàng)目的場(chǎng)景下的實(shí)現(xiàn)铝噩。

js 版

網(wǎng)上基本都是用的高階函數(shù)實(shí)現(xiàn)衡蚂,即封裝一個(gè)工具函數(shù) debounce,它以參數(shù)形式接收原函數(shù)骏庸,并返回一個(gè)經(jīng)過防抖處理的新函數(shù)毛甲,后續(xù)涉及到需要防抖處理的,都需要使用新函數(shù)來替代原函數(shù)具被。

function debounce(fn, delay = 200) {
    if (typeof fn !== 'function') { // 參數(shù)類型為函數(shù)
        throw new TypeError('fn is not a function');
    }
    
    let lastFn = null; 
    return function(...args) {
        if (lastFn) {
             clearTimeout(lastFn);
        }
        let lastFn = setTimeout(() => {
            lastFn = null;
            fn.call(this, ...args);
        }, delay);
    }
}

其實(shí)很簡(jiǎn)單玻募,就是每次調(diào)用函數(shù)前,先移除上次還處于延遲中的任務(wù)一姿,然后重新發(fā)起一次新的延遲等待七咧。

上面最重要的地方在于 fn.call(this, ...args)跃惫,這里之所以要通過 call 方式來修改原函數(shù)的 this,是因?yàn)榘埃瘮?shù)通過參數(shù)進(jìn)行傳遞時(shí)爆存,是只會(huì)被當(dāng)做普通函數(shù)處理,不管原函數(shù)本來是否掛載在某個(gè)對(duì)象上蝗砾。

所以先较,如果 debounce 內(nèi)部直接以 fn() 方式調(diào)用原函數(shù),會(huì)導(dǎo)致原函數(shù)的內(nèi)部 this 指向發(fā)生變化悼粮。

有兩種解決方式:

一是:debounce 以 fn() 方式調(diào)用闲勺,但在使用 debounce 的地方,傳遞 fn 原函數(shù)時(shí)需要先進(jìn)行綁定矮锈,如:

var o = {
    c: 1,
    a: function() {
        console.log(this.c);
    }
}
var b = debounce(o.a.bind(o));

這是一種方式,缺點(diǎn)是需要使用者手動(dòng)進(jìn)行顯示綁定 this睁蕾。

另一種方式:debounce 內(nèi)部通過 apply 或 call 方式來調(diào)用原函數(shù)苞笨。

但這種方式也有一個(gè)前提,就是 debounce 返回的新函數(shù)需要把它當(dāng)做原函數(shù)子眶,和原函數(shù)一樣的處理瀑凝。如果原函數(shù)本來掛載在某對(duì)象上,新生成的函數(shù)也需要掛載到那對(duì)象上臭杰,因?yàn)?debounce 內(nèi)部的 fn.call(this) 時(shí)粤咪,這個(gè) this 是指返回的新函數(shù)調(diào)用時(shí)的 this。所以渴杆,需要讓新函數(shù)的 this 和原函數(shù)是一致的寥枝,才會(huì)是期望的正常行為。

var o = {
    c: 1,
    a: function() {
        console.log(this.c);
    }
}

o.b = debounce(o.a);

總之磁奖,debounce 的用途就是通用的工具函數(shù)囊拜,所以需要防抖處理的工作,都可以通過 debounce 進(jìn)行包裝轉(zhuǎn)換比搭。

就算你沒寫過這個(gè)通用的工具函數(shù)冠跷,至少在項(xiàng)目中,也寫過直接定義一個(gè)全局變量來進(jìn)行防抖處理吧身诺,類似這樣:

var flag = null;

function task() {
    if (flag) {
        clearTimeout(flag);
    }
    flag = setTimeout(() => {
        flag = null;
        // do something
    }, 200);
}

這其實(shí)也是防抖的處理蜜托,只是實(shí)現(xiàn)方式是直接對(duì)需要進(jìn)行防抖處理的函數(shù),在其代碼基礎(chǔ)上霉赡,直接進(jìn)行改動(dòng)橄务。不具有通用性。

所以我才說穴亏,網(wǎng)上大眾版的 debounce 防抖函數(shù)仪糖,也許你沒接觸過柑司,也沒見過,但不代表你沒接觸到防抖處理的思想锅劝,在實(shí)際項(xiàng)目中攒驰,其實(shí)或多或少都會(huì)有所接觸了,只是實(shí)現(xiàn)的方式故爵、通用性等不一樣而已玻粪。

當(dāng)然,以上的 js 版實(shí)現(xiàn)诬垂,只是一種最基礎(chǔ)的方案劲室,文章開頭給出的鏈接中,還有很多擴(kuò)展的實(shí)現(xiàn)结窘,比如增加了支持第一次觸發(fā)立即執(zhí)行的功能很洋;和 throttle 節(jié)流結(jié)合用法;手動(dòng)取消延遲的功能等等隧枫。

感興趣的可以自行查閱喉磁,我是覺得,大概知道基礎(chǔ)思想就夠了官脓,實(shí)際項(xiàng)目中再根據(jù)需要去進(jìn)行擴(kuò)展协怒。

ts + angular 版

我還想講講我在實(shí)際項(xiàng)目中所進(jìn)行的防抖處理,上面的 js 版在每篇防抖文章中卑笨,基本都是那樣實(shí)現(xiàn)孕暇,都是封裝一個(gè)高階函數(shù)。

但我實(shí)際開發(fā)中赤兴,使用的是 TypeScript妖滔,這是一種類似于 Java 思想的強(qiáng)類型語(yǔ)言,所以很少會(huì)用到高階函數(shù)的思想桶良,更多的是封裝工具類铛楣。

再加上,我框架是使用 angular艺普,項(xiàng)目中除了有防抖處理的場(chǎng)景簸州,還有其他諸如延遲任務(wù)的場(chǎng)景,輪詢?nèi)蝿?wù)的場(chǎng)景等等歧譬。這些不管是從用法岸浑、實(shí)現(xiàn)上等來說,都很相似瑰步,所以我都統(tǒng)一封裝在一起矢洲。

另外,涉及 setTimeout缩焦,setInterval 這兩個(gè) API读虏,如果沒有進(jìn)行清理工作责静,很容易造成內(nèi)存泄漏,因此跟 setTimeout 和 setInterval 相關(guān)的用法盖桥,我都將它跟 angular 的組件進(jìn)行綁定處理灾螃,避免開發(fā)人員忘記清理,至少我還可以在組件銷毀時(shí)去自動(dòng)清理揩徊。

export class PollingTaskUtils {
    constructor(){}
    
    static tag(component: {ngOnDestroy}, tag: string = 'default'): PollingMgr {
        let taskTag = `__${tag}__`;
        if (component[taskTag] == null) {
            let pollingMgr = new PollingMgr(component, taskTag);
            component[taskTag] = pollingMgr;
        }
        return component[taskTag];
    }
}

export class PollingMgr {
    private readonly AUTO_CLEAR_FLAG_PREFIX = '__auto_clear_flat';
    
    private _delay: number = 0; // 任務(wù)的延遲時(shí)長(zhǎng)
    private _isLoop: boolean = false; // 是否是循環(huán)任務(wù)
    private _interval: number;  // 循環(huán)任務(wù)的間隔
    private _resolve: {loop: (interval?: number) => any}
    private _pollingTask: (resolve?: {loop: (interval?: number) => any}) => any;
    private _pollingFlag = null;
    private _pollingIntervalFlag = null;
    
    constructor(protected component: {ngOnDestroy}, protected tag: string) {
        this._resolve = {
            loop: (interval?: number) => {
                if (interval > 0) {
                    this._isLoop = true;
                    this._interval = interval;
                } else {
                    this._isLoop = false;
                }
                this._handleLoop();
            }
        };
    }
    
    run(task: (resolve?: {loop: (interval?: number) => any}) => any) {
        this._clear(this.component);
        this._pollingTask = task;
        if (this._delay) {
            this._pollingFlag = setTimeout(() => {
                this._pollingFlag = null;
                task.apply(this.component, [this._resolve]);
            }, this._delay);
        } else {
            task.apply(this.component, [this._resolve]);
        }
        this._handleAutoClear();
    }
    
    clear(component?: {ngOnDestroy}) {
        this._clear(component);
    }
    
    delay(dealy: number): PollingMgr {
        this._delay = delay;
        return this;
    }
    
    runInterval(task: () => any, interval: number) {
        if (!this._pollingIntervalFlag) {
            this._pollingIntervalFlag = setInterval(() => {
                task.apply(this.component);
            }, interval);
        }
        this._handleAutoClear();
        return this._pollingIntervalFlag;
    }
    
    private _handleLoop() {
        if (this._isLoop) {
            this._clear(this.component);
            this._pollingFlag = setTimeout(() => {
                this._pollingFlag = null;
                this._pollingTask.apply(this.component, [this._resolve]);
            }, this._interval);
        }
    }
    
    private _handleAutoClear() {
        if (this.component[this.AUTO_CLEAR_FLAG_PREFIX + this.tag] == null) {
            this.component[this.AUTO_CLEAR_FLAG_PREFIX + this.tag] = true;
            let originFun = this.component['ngOnDestroy'];
            this.component['ngOnDestroy'] = (): void => {
                originFun.apply(this.component);
                delete this.component[this.tag];
                delete this.component[this.AUTO_CLEAR_FLAG_PREFIX + this.tag]腰鬼;
                this._pollingTask = null;
                this._clear(this.component);
                if (this._pollingIntervalFlag) {
                    clearInterval(this._pollingIntervalFlag);
                    this._pollingIntervalFlag = null;
                }
            };
        }
    }
    
    private _clear(component: {ngOnDestroy}) {
        this._isLoop = false;
        if (this._pollingFlag) {
            clearTimeout(this._pollingFlag);
            this._pollingFlag = null;
        }
    }
}

當(dāng)初封裝的時(shí)候沒有寫注釋,感興趣的再細(xì)看吧塑荒,這里就是做個(gè)記錄熄赡,方便后續(xù)查閱,下面看看用法:

/**
* 輪詢齿税、延遲彼硫、防抖的任務(wù)工具類
* 入口接收兩個(gè)參數(shù):
* component:當(dāng)前的組件類,使用時(shí)必須掛載在某個(gè)組件上凌箕,在組件銷戶時(shí)拧篮,如果有輪詢?nèi)蝿?wù),會(huì)去進(jìn)行釋放定時(shí)器
* tag:可選參數(shù)陌知,用于標(biāo)識(shí)不同的任務(wù)他托,相同的 tag掖肋,多次調(diào)用都會(huì)被視為同個(gè)任務(wù)進(jìn)行防抖處理
*/
PollingTaskUtils.tag(component, tag?: string);

// 1. 延遲任務(wù)用法仆葡,比如延遲5s后處理
PollingTaskUtils.tag(this).delay(5000).run(() => {
    // do something
});
// 因?yàn)?tag 沒傳,該任務(wù)會(huì)和上面的被視為同個(gè)任務(wù)志笼,如果上個(gè)任務(wù)延遲未被執(zhí)行沿盅,則先取消,以下面為主
PollingTaskUtils.tag(this).delay(5000).run(() => {
    // do something
});
// tag 參數(shù)指定為 'task'纫溃,表示一個(gè)新的任務(wù)腰涧,會(huì)上述的延遲任務(wù)相互獨(dú)立
PollingTaskUtils.tag(this).delay(5000, 'task').run(() => {
    // do something
});

// 2. 輪詢?nèi)蝿?wù),比如每隔 10s 發(fā)起一次請(qǐng)求
PollingTaskUtils.tag(this).run(resolve => {
    // 模擬請(qǐng)求
    setTimeout(() => {
        // do something
        resolve.loop(10000); // 設(shè)置輪詢間隔
    }, 2000)
});

// 3. 輪詢?nèi)蝿?wù)紊浩,符合一定條件停止輪詢
PollingTaskUtils.tag(this).run(resolve => {
    // 模擬請(qǐng)求
    setTimeout(() => {
        if (flag) {
            resolve.loop(-1); // <=0 或者不調(diào)用時(shí)停止輪詢
        } else {
            resolve.loop(3000);
        }
    }, 2000);
});

// 4. 防抖處理
let i = 0;
while(i++ < 10) {
    PollingTaskUtils.tag(this).delay(500).run(() => {
        // do something
    });
}

// 5. 由于 run 內(nèi)部是通過 setTimeout 來實(shí)現(xiàn)輪詢?nèi)蝿?wù)窖铡,但這個(gè)并不精準(zhǔn),當(dāng)要求較精準(zhǔn)的輪詢時(shí)坊谁,比如時(shí)鐘费彼,使用 setInterval 會(huì)比較精準(zhǔn)
PollingTaskUtils.tag(this).runInterval(() => {
    // do something
}, 1000);  

其實(shí)用法跟直接用 setTimeout 和 setInterval 沒多大區(qū)別,但好處在于口芍,增加了跟組件的綁定箍铲,增加了對(duì)任務(wù)標(biāo)識(shí)的處理,這樣一來鬓椭,即使忘記清理颠猴,內(nèi)部也可以在組件銷毀時(shí)自動(dòng)去清理关划,即使多次調(diào)用,只要任務(wù)標(biāo)識(shí)不一樣翘瓮,內(nèi)部就會(huì)進(jìn)行防抖處理贮折。可以省掉一部分的工作量春畔。

當(dāng)然脱货,這些所有的出發(fā)點(diǎn),僅適用于我的項(xiàng)目律姨,因?yàn)楫吘故菑捻?xiàng)目中遇到的需求中來進(jìn)行封裝處理的振峻,并不一定適用于你。

我想說的是择份,這些工具函數(shù)的封裝扣孟,重要的是掌握其思想,為什么需要進(jìn)行防抖處理荣赶?防抖處理的基本實(shí)現(xiàn)是什么凤价?知道這些即可,其余的拔创,再自行根據(jù)需要擴(kuò)展學(xué)習(xí)利诺。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市剩燥,隨后出現(xiàn)的幾起案子慢逾,更是在濱河造成了極大的恐慌,老刑警劉巖灭红,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侣滩,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡变擒,警方通過查閱死者的電腦和手機(jī)君珠,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娇斑,“玉大人策添,你說我怎么就攤上這事『晾拢” “怎么了唯竹?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)悔醋。 經(jīng)常有香客問我摩窃,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任猾愿,我火速辦了婚禮鹦聪,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘蒂秘。我一直安慰自己泽本,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布姻僧。 她就那樣靜靜地躺著规丽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撇贺。 梳的紋絲不亂的頭發(fā)上赌莺,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天,我揣著相機(jī)與錄音松嘶,去河邊找鬼艘狭。 笑死,一個(gè)胖子當(dāng)著我的面吹牛翠订,可吹牛的內(nèi)容都是我干的巢音。 我是一名探鬼主播,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼尽超,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼官撼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起似谁,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤傲绣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后棘脐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體斜筐,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡龙致,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年蛀缝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片目代。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡屈梁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出榛了,到底是詐尸還是另有隱情在讶,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布霜大,位于F島的核電站构哺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜曙强,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一残拐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碟嘴,春花似錦溪食、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至雀瓢,卻和暖如春枢析,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背刃麸。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工登疗, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人嫌蚤。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓辐益,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親脱吱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子智政,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

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

  • 前言 最近和前端的小伙伴們,在討論面試題的時(shí)候箱蝠。談到了函數(shù)防抖和函數(shù)節(jié)流的應(yīng)用場(chǎng)景和原理续捂。于是,想深入研究一下兩者...
    youthcity閱讀 23,547評(píng)論 5 78
  • 函數(shù)節(jié)流場(chǎng)景 例如:實(shí)現(xiàn)一個(gè)原生的拖拽功能(如果不用H5 Drag和Drop API),我們就需要一路監(jiān)聽mous...
    凡凡的小web閱讀 785評(píng)論 0 0
  • 防抖和節(jié)流是針對(duì)響應(yīng)跟不上觸發(fā)頻率這類問題的兩種解決方案 debounce宦搬,去抖動(dòng)牙瓢。策略是當(dāng)事件被觸發(fā)時(shí),設(shè)定一個(gè)...
    皇甫圣坤閱讀 370評(píng)論 0 0
  • 前言 props與state都是用于組件存儲(chǔ)數(shù)據(jù)的一js對(duì)象,前者是對(duì)外暴露數(shù)據(jù)接口,后者是對(duì)內(nèi)組件的狀態(tài),它們決...
    itclanCoder閱讀 2,156評(píng)論 0 0
  • 文——梁花花 我總站在原地间校, 腳下踩著黝黑的土地 我把田地隆平矾克, 打上整齊的溝渠! 夏出的稻子開始抽出嫩黃的穗兒憔足,...
    衣飾憶流年閱讀 180評(píng)論 7 6