亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

JS面試之手寫節(jié)流防抖詳解

 更新時(shí)間:2023年07月31日 11:28:13   作者:前端平水  
作為一個(gè)程序員,代碼實(shí)現(xiàn)才是能力體現(xiàn),在大部分面試的時(shí)候,我們都會(huì)被要求手寫代碼實(shí)現(xiàn)一個(gè)功能,本文總結(jié)了一下經(jīng)常被面試官問到的節(jié)流和防抖功能的實(shí)現(xiàn),分享給有需要的小伙伴

前言

作為一個(gè)程序員,代碼實(shí)現(xiàn)才是能力體現(xiàn),在大部分面試的時(shí)候,我們都會(huì)被要求手寫代碼實(shí)現(xiàn)一個(gè)功能,這需要有良好的代碼習(xí)慣和思路,有時(shí)候我們也可以多去看看和理解一些經(jīng)常用到的api源代碼,這是有幫助的。這里我總結(jié)了一下經(jīng)常被面試官問到的節(jié)流和防抖功能的實(shí)現(xiàn),分享給有需要的小伙伴

防抖

防抖,就是防止頻繁重復(fù)的觸發(fā)某個(gè)事件。比如當(dāng)我們點(diǎn)擊一個(gè)按鈕觸發(fā)一個(gè)事件時(shí),可能會(huì)連續(xù)點(diǎn)了幾次,那么會(huì)連續(xù)多次觸發(fā)事件函數(shù),這是一種浪費(fèi),尤其是對(duì)于異步請(qǐng)求數(shù)據(jù),連續(xù)的請(qǐng)求數(shù)據(jù)很大程度會(huì)影響性能和用戶體驗(yàn)。所以,防抖,就是在觸發(fā)一次函數(shù)后規(guī)定時(shí)間內(nèi)沒有再次觸發(fā),再執(zhí)行該事件函數(shù),若多次在規(guī)定時(shí)間內(nèi)重復(fù)觸發(fā),只執(zhí)行最后一次觸發(fā)

實(shí)現(xiàn)思路

首先,我們要知道,防抖函數(shù)是一個(gè)工具函數(shù),我們需要將目標(biāo)事件函數(shù)作為參數(shù)傳入防抖函數(shù),防抖函數(shù)內(nèi)要返回一個(gè)調(diào)用了目標(biāo)函數(shù)的函數(shù),當(dāng)調(diào)用防抖函數(shù)時(shí),讓防抖函數(shù)調(diào)用目標(biāo)事件函數(shù),來實(shí)現(xiàn)防抖功能。其次,假如從一個(gè)中級(jí)程序員角度出發(fā),應(yīng)該想到當(dāng)我們把自己寫的防抖函數(shù)作為一個(gè)api給初級(jí)程序員使用時(shí),希望可以主動(dòng)設(shè)置防抖的時(shí)間間隔,所以這個(gè)時(shí)間間隔也應(yīng)該是防抖函數(shù)的一個(gè)參數(shù)。

function debounce(fn,wait){
    return function(){
        fn()
    }
}

在這個(gè)防抖函數(shù)內(nèi),我們要調(diào)用這個(gè)傳入的目標(biāo)事件函數(shù),除此之外,我們還要根據(jù)參數(shù)設(shè)置防抖等待的時(shí)間,觸發(fā)一次函數(shù)后,要等待該時(shí)間間隔再執(zhí)行該目標(biāo)事件函數(shù)。所以我們想到使用計(jì)時(shí)器setTimeout(),調(diào)用目標(biāo)函數(shù),并且設(shè)置等待時(shí)間。當(dāng)我們?cè)诘却龝r(shí)間內(nèi)再次觸發(fā)防抖函數(shù),就需要清除之前的計(jì)時(shí)器,重新等待時(shí)間,那么就需要給定時(shí)器定義變量名,且調(diào)用清除定時(shí)器函數(shù)。

*注:定義定時(shí)器變量名需要在返回的函數(shù)外部,形成閉包,否則每次觸發(fā)防抖函數(shù)調(diào)用目標(biāo)事件函數(shù)會(huì)重新定義一個(gè)同名空變量,導(dǎo)致無法清除之前定時(shí)器

function debounce(fn,wait){
    let timeout
    return function(){
        clearTimeout(timeout)
        timeout = setTimeout(fn,wait)
    }
}

到這里我們已經(jīng)完成了大體功能,之后需要我們考慮更多使用這個(gè)防抖函數(shù)時(shí)可能遇到細(xì)節(jié)情況:

比如定義目標(biāo)事件函數(shù)時(shí),可能會(huì)使用this來指向觸發(fā)這個(gè)事件函數(shù)的DOM結(jié)構(gòu),但是讓該目標(biāo)事件函數(shù)在防抖函數(shù)中的定時(shí)器中調(diào)用時(shí),會(huì)改變?cè)摵瘮?shù)內(nèi)的this指向window,影響到原函數(shù),所以就需要改正調(diào)用該目標(biāo)函數(shù)的this指向。所以可以使用顯示綁定(call、apply、bind)來維護(hù)目標(biāo)事件函數(shù)的this指向。在定時(shí)器調(diào)用目標(biāo)事件函數(shù)時(shí),將該函數(shù)顯示綁定到防抖函數(shù)的this,因?yàn)楫?dāng)防抖函數(shù)被觸發(fā)時(shí),它的this也是指向觸發(fā)這個(gè)函數(shù)的DOM結(jié)構(gòu)

function debounce(fn,wait){
    let timeout
    return function(){
        clearTimeout(timeout)
        timeout = setTimeout(()=>{ //改成箭頭函數(shù),否則不能直接接.call()
            fn.call(this)
        },wait)
    }
}

還需要考慮到定義的目標(biāo)事件函數(shù)本身也可能需要傳遞參數(shù),比如可能是事件參數(shù)e,也可能有多個(gè)參數(shù)。所以我們需要解構(gòu)出目標(biāo)事件函數(shù)可能接收的所有函數(shù)。使用防抖函數(shù)調(diào)用目標(biāo)事件函數(shù)時(shí),只能將需要傳遞的參數(shù)傳遞給防抖函數(shù),再?gòu)姆蓝逗瘮?shù)解構(gòu)出參數(shù),來傳遞給目標(biāo)事件函數(shù)。arguments就是用來代表函數(shù)接受的所有參數(shù),我們就可以用它解構(gòu)防抖函數(shù)接收到的參數(shù)。

*注:arguments是一個(gè)類數(shù)組,需要將其解構(gòu)出來再變成數(shù)組,然后作為參數(shù)傳遞給目標(biāo)函數(shù)。并且輔助函數(shù)call()接收參數(shù)的方式是逐個(gè)接收,所以我們需要改成apply(),來接受數(shù)組作為參數(shù)

function debounce(fn,wait){
    let timeout
    return function(){
        let args = [...arguments]
        clearTimeout(timeout)
        timeout = setTimeout(()=>{
            fn.apply(this,args)
        },wait)
    }
}

最后,還需要考慮到,定義的目標(biāo)事件函數(shù)可能有返回值,所以我們還需要定義一個(gè)變量來接收目標(biāo)時(shí)間函數(shù)調(diào)用后的返回值,并返回這個(gè)變量

function debounce(fn,wait){
    let timeout,result //同樣定義在返回函數(shù)外,形成閉包
    return function(){
        let args = [...arguments]
        clearTimeout(timeout)
        timeout = setTimeout(()=>{
            result = fn.apply(this,args)
        },wait)
        return result
    }
}

到這里,一個(gè)完整的防抖函數(shù)就實(shí)現(xiàn)了,我們可以嘗試簡(jiǎn)單驗(yàn)證一下,源代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="btn">0</button>
    <script>
        let count = 0
        let btn = document.getElementById("btn")
        function add(){
            this.innerHTML = ++count
        }
        function debounce(fn,wait){
            var timeout,result
            return function(){
                let args = [...arguments]
                clearTimeout(timeout)
                timeout = setTimeout(()=>{
                    result = fn.apply(this,args)
                },wait)
                return result
            }
        }
        btn.addEventListener('click',debounce(add,1000))
    </script>
</body>
</html>

節(jié)流

節(jié)流,和防抖類似,都是防止一些不必要的操作,區(qū)別就是,防抖是在規(guī)定時(shí)間內(nèi)一直重復(fù)觸發(fā)函數(shù),就一直不執(zhí)行,直到在規(guī)定時(shí)間內(nèi)沒有再次觸發(fā),才執(zhí)行;而節(jié)流是在規(guī)定時(shí)間內(nèi)一直重復(fù)觸發(fā)函數(shù),那么該規(guī)定時(shí)間內(nèi)只會(huì)執(zhí)行其中一次。比如,當(dāng)點(diǎn)擊觸發(fā)一個(gè)目標(biāo)事件函數(shù),可以直接執(zhí)行這個(gè)函數(shù),但是當(dāng)我們?cè)谝?guī)定時(shí)間內(nèi)又多次重復(fù)觸發(fā)該函數(shù),那么就不執(zhí)行之后的觸發(fā),而規(guī)定時(shí)間之后又有觸發(fā)該函數(shù),就又執(zhí)行該函數(shù),并且從這個(gè)函數(shù)觸發(fā)的時(shí)間開始重新計(jì)算時(shí)間,之后規(guī)定時(shí)間內(nèi)的重復(fù)觸發(fā)也不會(huì)被執(zhí)行,這就是節(jié)流

實(shí)現(xiàn)思路

節(jié)流的實(shí)現(xiàn)思路大體一樣,是一個(gè)工具函數(shù),需要傳入目標(biāo)事件函數(shù)和規(guī)定時(shí)間作為參數(shù),并且返回一個(gè)函數(shù)調(diào)用這個(gè)目標(biāo)事件函數(shù),但是這里不需要使用定時(shí)器setTimeout(),需要定義一個(gè)開始時(shí)間,當(dāng)點(diǎn)擊觸發(fā)一個(gè)函數(shù)時(shí),將此時(shí)的時(shí)間賦值給開始時(shí)間,之后每次觸發(fā)都判斷此時(shí)距離開始時(shí)間間隔是否超過規(guī)定時(shí)間,若超過就執(zhí)行函數(shù),并且將當(dāng)前時(shí)間又賦值給開始時(shí)間,若未超過,則不執(zhí)行函數(shù)也不重新賦值開始時(shí)間

function throttle(fn,wait){
    let preTime = 0  //同樣使用閉包,保留這個(gè)變量
    return function(){
        let now = +new Date()  //一元運(yùn)算符+改為秒數(shù)時(shí)間
        if(now - preTime > wait){
            fn()
            preTime = now
        }
    }
}

另外,同樣需要考慮目標(biāo)事件函數(shù)中的this指向,以及目標(biāo)事件函數(shù)本身需要接收的參數(shù),和可能有返回值得情況。值得注意的是,雖然這里沒有使用setTimeout()定時(shí)器,但是函數(shù)調(diào)用在返回出來的函數(shù)中,其內(nèi)部this指向也會(huì)被改變,受到影響,所以需要用apply()輔助函數(shù)顯示綁定,改正this指向

function throttle(fn,wait){
    let preTime = 0,result
    return function(){
        let args = [...arguments]
        let now = +new Date()
        if(now - preTime > wait){
            result = fn.apply(this,args)
            preTime = now
        }
        return result
    }
}

到這里,也完成了節(jié)流函數(shù)的手寫實(shí)現(xiàn),可以測(cè)試一下,源代碼:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    <button id="btn">0</button>
    <script>
        let count = 0
        let btn = document.getElementById("btn")
        function add(){
            this.innerHTML = ++count
        }
        function throttle(fn,wait){
            let preTime = 0,result
            return function(){
                let args = [...arguments]
                let now = +new Date()
                if(now - preTime > wait){
                    result = fn.apply(this,args)
                    preTime = now
                }
                return result
            }
        }
        btn.addEventListener('click',throttle(add,1000))
    </script>
</body>
</html>

總結(jié)

在面試中,要求手寫實(shí)現(xiàn)功能是經(jīng)常遇到的,而防抖和節(jié)流函數(shù)又是經(jīng)常被要求書寫的函數(shù)之一,大家應(yīng)該也會(huì)覺不難,當(dāng)然寫出來也是幫助有需要的小伙伴,希望大家有所收獲,面試一舉拿下

到此這篇關(guān)于JS面試之手寫節(jié)流防抖詳解的文章就介紹到這了,更多相關(guān)JS節(jié)流防抖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論