JavaScript定時(shí)器/防抖/節(jié)流實(shí)例詳解
定時(shí)器之 `setTimeout()`
JavaScript 提供定時(shí)執(zhí)行代碼的功能,叫做定時(shí)器(timer),主要由 `setTimeout()`和 `setInterval()`這兩個(gè)函數(shù)來(lái)完成。它們向任務(wù)隊(duì)列添加定時(shí)任務(wù)
`setTimeout`函數(shù)用來(lái)指定某個(gè)函數(shù)或某段代碼,在多少毫秒之后執(zhí)行。它返回一個(gè)整數(shù),表示定時(shí)器的編號(hào),以后可以用來(lái)取消這個(gè)定時(shí)器。
var timerId = setTimeout(func|code, delay);
`setTimeout`函數(shù)接受兩個(gè)參數(shù),第一個(gè)參數(shù) `func|code`是將要推遲執(zhí)行的函數(shù)名或者一段代碼,第二個(gè)參數(shù) `delay`是推遲執(zhí)行的毫秒數(shù)
setTimeout(function(){
console.log("定時(shí)器")
},1000)溫馨提示
還有一個(gè)需要注意的地方,如果回調(diào)函數(shù)是對(duì)象的方法,那么 `setTimeout`使得方法內(nèi)部的 `this`關(guān)鍵字指向全局環(huán)境,而不是定義時(shí)所在的那個(gè)對(duì)象
var name = "sxt";
var user = {
name: "itbaizhan",
getName: function () {
setTimeout(function(){
console.log(this.name);
},1000)
}
};
user.getName();解決方案
var name = "sxt";
var user = {
name: "itbaizhan",
getName: function () {
var that = this;
setTimeout(function(){
console.log(that.name);
},1000)
}
};
user.getName();定時(shí)器可以進(jìn)行取消
var id = setTimeout(f, 1000); clearTimeout(id);
定時(shí)器之 `setInterval()`
`setInterval`函數(shù)的用法與 `setTimeout`完全一致,區(qū)別僅僅在于 `setInterval`指定某個(gè)任務(wù)每隔一段時(shí)間就執(zhí)行一次,也就是無(wú)限次的定時(shí)執(zhí)行
var timer = setInterval(function() {
console.log(2);
}, 1000)通過(guò)setInterval方法實(shí)現(xiàn)網(wǎng)頁(yè)動(dòng)畫(huà)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
#someDiv{
width: 100px;
height: 100px;
background: red;
}
</style>
</head>
<body>
<div id="someDiv"></div>
<script>
var div = document.getElementById('someDiv');
var opacity = 1;
var fader = setInterval(function() {
opacity -= 0.05;
if (opacity > 0) {
div.style.opacity = opacity;
} else {
clearInterval(fader);
}
}, 30);
</script>
</body>
</html>
定時(shí)器可以進(jìn)行取消
var id = setInterval(f, 1000); clearInterval(id);
防抖(debounce)
防抖嚴(yán)格算起來(lái)應(yīng)該屬于性能優(yōu)化的知識(shí),但實(shí)際上遇到的頻率相當(dāng)高,處理不當(dāng)或者放任不管就容易引起瀏覽器卡死。
從滾動(dòng)條監(jiān)聽(tīng)的例子說(shuō)起
function showTop () {
var scrollTop = document.documentElement.scrollTop;
console.log('滾動(dòng)條位置:' + scrollTop);
}
window.onscroll = showTop在運(yùn)行的時(shí)候會(huì)發(fā)現(xiàn)存在一個(gè)問(wèn)題:這個(gè)函數(shù)的默認(rèn)執(zhí)行頻率,太!高!了!。 高到什么程度呢?以chrome為例,我們可以點(diǎn)擊選中一個(gè)頁(yè)面的滾動(dòng)條,然后點(diǎn)擊一次鍵盤(pán)的【向下方向鍵】,會(huì)發(fā)現(xiàn)函數(shù)執(zhí)行了8-9次!
然而實(shí)際上我們并不需要如此高頻的反饋,畢竟瀏覽器的性能是有限的,不應(yīng)該浪費(fèi)在這里,所以接著討論如何優(yōu)化這種場(chǎng)景。
基于上述場(chǎng)景,首先提出第一種思路:在第一次觸發(fā)事件時(shí),不立即執(zhí)行函數(shù),而是給出一個(gè)期限值比如200ms,然后
1.如果在200ms內(nèi)沒(méi)有再次觸發(fā)滾動(dòng)事件,那么就執(zhí)行函數(shù)
2.如果在200ms內(nèi)再次觸發(fā)滾動(dòng)事件,那么當(dāng)前的計(jì)時(shí)取消,重新開(kāi)始計(jì)時(shí)
效果:如果短時(shí)間內(nèi)大量觸發(fā)同一事件,只會(huì)執(zhí)行一次函數(shù)
實(shí)現(xiàn):既然前面都提到了計(jì)時(shí),那實(shí)現(xiàn)的關(guān)鍵就在于setTimeout這個(gè)函數(shù),由于還需要一個(gè)變量來(lái)保存計(jì)時(shí),考慮維護(hù)全局純凈,可以借助閉包來(lái)實(shí)現(xiàn)
function debounce(fn,delay){
let timer = null //借助閉包
return function() {
if(timer){
clearTimeout(timer)
}
timer = setTimeout(fn,delay) // 簡(jiǎn)化寫(xiě)法
}
}
// 然后是舊代碼
function showTop () {
var scrollTop = document.documentElement.scrollTop;
console.log('滾動(dòng)條位置:' + scrollTop);
}
window.onscroll = debounce(showTop,300)到這里,已經(jīng)把防抖實(shí)現(xiàn)了
防抖定義
對(duì)于短時(shí)間內(nèi)連續(xù)觸發(fā)的事件(上面的滾動(dòng)事件),防抖的含義就是讓某個(gè)時(shí)間期限(如上面的1000毫秒)內(nèi),事件處理函數(shù)只執(zhí)行一次
節(jié)流(throttle)
節(jié)流嚴(yán)格算起來(lái)應(yīng)該屬于性能優(yōu)化的知識(shí),但實(shí)際上遇到的頻率相當(dāng)高,處理不當(dāng)或者放任不管就容易引起瀏覽器卡死
繼續(xù)思考,使用上面的防抖方案來(lái)處理問(wèn)題的結(jié)果是
如果在限定時(shí)間段內(nèi),不斷觸發(fā)滾動(dòng)事件(比如某個(gè)用戶(hù)閑著無(wú)聊,按住滾動(dòng)不斷的拖來(lái)拖去),只要不停止觸發(fā),理論上就永遠(yuǎn)不會(huì)輸出當(dāng)前距離頂部的距離
但是如果產(chǎn)品同學(xué)的期望處理方案是:即使用戶(hù)不斷拖動(dòng)滾動(dòng)條,也能在某個(gè)時(shí)間間隔之后給出反饋呢?
其實(shí)很簡(jiǎn)單:我們可以設(shè)計(jì)一種類(lèi)似控制閥門(mén)一樣定期開(kāi)放的函數(shù),也就是讓函數(shù)執(zhí)行一次后,在某個(gè)時(shí)間段內(nèi)暫時(shí)失效,過(guò)了這段時(shí)間后再重新激活(類(lèi)似于技能冷卻時(shí)間)
效果:如果短時(shí)間內(nèi)大量觸發(fā)同一事件,那么在函數(shù)執(zhí)行一次之后,該函數(shù)在指定的時(shí)間期限內(nèi)不再工作,直至過(guò)了這段時(shí)間才重新生效
實(shí)現(xiàn)
這里借助setTimeout來(lái)做一個(gè)簡(jiǎn)單的實(shí)現(xiàn),加上一個(gè)狀態(tài)位valid來(lái)表示當(dāng)前函數(shù)是否處于工作狀態(tài)
function throttle(fn,delay){
let valid = true
return function() {
if(!valid){
//休息時(shí)間 暫不接客
return false
}
// 工作時(shí)間,執(zhí)行函數(shù)并且在間隔期內(nèi)把狀態(tài)位設(shè)為無(wú)效
valid = false
setTimeout(function(){
fn()
valid = true;
}, delay)
}
}
function showTop () {
var scrollTop = document.documentElement.scrollTop;
console.log('滾動(dòng)條位置:' + scrollTop);
}
window.onscroll = throttle(showTop,300)
如果一直拖著滾動(dòng)條進(jìn)行滾動(dòng),那么會(huì)以300ms的時(shí)間間隔,持續(xù)輸出當(dāng)前位置和頂部的距離
講完了這兩個(gè)技巧,下面介紹一下平時(shí)開(kāi)發(fā)中常遇到的場(chǎng)景:
(1)搜索框input事件,例如要支持輸入實(shí)時(shí)搜索可以使用節(jié)流方案(間隔一段時(shí)間就必須查詢(xún)相關(guān)內(nèi)容),或者實(shí)現(xiàn)輸入間隔大于某個(gè)值(如500ms),就當(dāng)做用戶(hù)輸入完成,然后開(kāi)始搜索,具體使用哪種方案要看業(yè)務(wù)需求
(2)頁(yè)面resize事件,常見(jiàn)于需要做頁(yè)面適配的時(shí)候。需要根據(jù)最終呈現(xiàn)的頁(yè)面情況進(jìn)行dom渲染(這種情形一般是使用防抖,因?yàn)橹恍枰袛嘧詈笠淮蔚淖兓闆r)
總結(jié)
到此這篇關(guān)于JavaScript定時(shí)器/防抖/節(jié)流的文章就介紹到這了,更多相關(guān)JS定時(shí)器/防抖/節(jié)流內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Javascript的常規(guī)數(shù)組和關(guān)聯(lián)數(shù)組對(duì)比小結(jié)
關(guān)聯(lián)數(shù)組雖然可以用字符串作下標(biāo),但是這個(gè)下標(biāo)是不支持參數(shù)傳值的,換言之,你需要什么就取什么,聽(tīng)起來(lái)很智能,實(shí)際上你取值仍然需要你手動(dòng)去寫(xiě)下標(biāo)的2012-05-05
微信小程序開(kāi)發(fā)之麥克風(fēng)動(dòng)畫(huà) 幀動(dòng)畫(huà) 放大 淡出
本篇文章主要介紹了微信小程序開(kāi)發(fā)之麥克風(fēng)動(dòng)畫(huà):幀動(dòng)畫(huà)、放大、淡出的相關(guān)資料。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-04-04
淺析JavaScriptSerializer類(lèi)的序列化與反序列化
本篇文章主要介紹了JavaScriptSerializer類(lèi) 對(duì)象序列化與反序列化的方法、屬性以及實(shí)例代碼,有需要的朋友可以參考一下2016-11-11
json屬性名為什么要雙引號(hào)(個(gè)人猜測(cè))
json屬性名為什么要雙引號(hào)?更加規(guī)范,利于解析、避免class等關(guān)鍵字引起的不兼容問(wèn)題,需要的朋友可以參考下2014-07-07
Javascript? Constructor構(gòu)造器模式與Module模塊模式
這篇文章主要介紹了Javascript? Constructor構(gòu)造器模式與Module模塊模式,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-08-08

