JS中setTimeout的巧妙用法前端函數(shù)節(jié)流
來(lái)源:易賢網(wǎng) 閱讀:1061 次 日期:2016-07-16 14:10:31
溫馨提示:易賢網(wǎng)小編為您整理了“JS中setTimeout的巧妙用法前端函數(shù)節(jié)流”,方便廣大網(wǎng)友查閱!

這篇文章主要介紹了JS中setTimeout的巧妙用法前端函數(shù)節(jié)流 的相關(guān)資料,需要的朋友可以參考下

什么是函數(shù)節(jié)流?

函數(shù)節(jié)流簡(jiǎn)單的來(lái)說(shuō)就是不想讓該函數(shù)在很短的時(shí)間內(nèi)連續(xù)被調(diào)用,比如我們最常見的是窗口縮放的時(shí)候,經(jīng)常會(huì)執(zhí)行一些其他的操作函數(shù),比如發(fā)一個(gè)ajax請(qǐng)求等等事情,那么這時(shí)候窗口縮放的時(shí)候,有可能連續(xù)發(fā)多個(gè)請(qǐng)求,這并不是我們想要的,或者是說(shuō)我們常見的鼠標(biāo)移入移出tab切換效果,有時(shí)候連續(xù)且移動(dòng)的很快的時(shí)候,會(huì)有閃爍的效果,這時(shí)候我們就可以使用函數(shù)節(jié)流來(lái)操作。大家都知道,DOM的操作會(huì)很消耗或影響性能的,如果是說(shuō)在窗口縮放的時(shí)候,為元素綁定大量的dom操作的話,會(huì)引發(fā)大量的連續(xù)計(jì)算,比如在IE下,過(guò)多的DOM操作會(huì)影響瀏覽器性能,甚至嚴(yán)重的情況下,會(huì)引起瀏覽器崩潰的發(fā)生。這個(gè)時(shí)候我們就可以使用函數(shù)節(jié)流來(lái)優(yōu)化代碼了~

函數(shù)節(jié)流的基本原理:

使用一個(gè)定時(shí)器,先延時(shí)該函數(shù)的執(zhí)行,比如使用setTomeout()這個(gè)函數(shù)延遲一段時(shí)間后執(zhí)行函數(shù),如果在該時(shí)間段內(nèi)還觸發(fā)了其他事件,我們可以使用清除方法 clearTimeout()來(lái)清除該定時(shí)器,再setTimeout()一個(gè)新的定時(shí)器延遲一會(huì)兒執(zhí)行。

最近在某團(tuán)隊(duì)忙于一個(gè)項(xiàng)目,有這么一個(gè)頁(yè)面,采用傳統(tǒng)模式開發(fā)(吐槽它為什么不用React),它的DOM操作比較多,然后性能是比較差的,尤其當(dāng)你縮放窗口時(shí),可怕的事情發(fā)生了,出現(xiàn)了卡頓,甚至瀏覽器癱瘓。為什么呢?

由于該頁(yè)面的DOM操作非常多,故窗口縮放每一幀時(shí)都會(huì)觸發(fā)函數(shù)的執(zhí)行,連續(xù)的重新DOM操作,這樣對(duì)瀏覽器的開銷是非常大的。既然在窗口縮放時(shí),會(huì)讓瀏覽器重新計(jì)算DOM,那么我們?yōu)槭裁床豢梢宰孌OM的計(jì)算延時(shí)呢,讓窗口停止縮放后才重新計(jì),這樣不就節(jié)省了瀏覽器的開銷,達(dá)到優(yōu)化的效果了嗎?

知識(shí)準(zhǔn)備

1. setTimeout(code,millisec) 當(dāng)然就是本文的主角了。

setTimeout() 方法用于在指定的毫秒數(shù)后調(diào)用函數(shù)或計(jì)算表達(dá)式。

code必需。要調(diào)用的函數(shù)后要執(zhí)行的 JavaScript 代碼串。

millisec必需。在執(zhí)行代碼前需等待的毫秒數(shù)。

提示:setTimeout() 只執(zhí)行 code 一次。如果要多次調(diào)用,請(qǐng)使用 setInterval() 或者讓 code 自身再次調(diào)用 setTimeout()。

廣泛應(yīng)用于定時(shí)器,輪播圖,動(dòng)畫效果,自動(dòng)滾動(dòng)等等。

2. clearTimeout(id_of_setTimeout)

參數(shù) id_of_settimeout由 setTimeout() 返回的 ID 值。該值標(biāo)識(shí)要取消的延遲執(zhí)行代碼塊。

3. fun.apply(thisArg[, argsArray])

apply() 方法在指定 this 值和參數(shù)(參數(shù)以數(shù)組或類數(shù)組對(duì)象的形式存在)的情況下調(diào)用某個(gè)函數(shù)

該函數(shù)的語(yǔ)法與call()方法幾乎相同,唯一的區(qū)別在于,call()方法接受的是一個(gè)參數(shù)列表,而apply()接受的是一個(gè)包含多個(gè)參數(shù)數(shù)組的(或類數(shù)組對(duì)象)。

參數(shù)

thisArg

在 fun 函數(shù)運(yùn)行時(shí)指定的 this 值。需要注意的是,指定的 this 值并不一定是該函數(shù)執(zhí)行時(shí)真正的 this 值,如果這個(gè)函數(shù)處于非嚴(yán)格模式下,則指定為 null 或 undefined 時(shí)會(huì)自動(dòng)指向全局對(duì)象(瀏覽器中就是window對(duì)象),同時(shí)值為原始值(數(shù)字,字符串,布爾值)的 this 會(huì)指向該原始值的自動(dòng)包裝對(duì)象。

argsArray

一個(gè)數(shù)組或者類數(shù)組對(duì)象,其中的數(shù)組元素將作為單獨(dú)的參數(shù)傳給 fun 函數(shù)。如果該參數(shù)的值為null 或 undefined,則表示不需要傳入任何參數(shù)。從ECMAScript 5 開始可以使用類數(shù)組對(duì)象。

在調(diào)用一個(gè)存在的函數(shù)時(shí),你可以為其指定一個(gè) this 對(duì)象。 this 指當(dāng)前對(duì)象,也就是正在調(diào)用這個(gè)函數(shù)的對(duì)象。 使用 apply, 你可以只寫一次這個(gè)方法然后在另一個(gè)對(duì)象中繼承它,而不用在新對(duì)象中重復(fù)寫該方法。

4. fun.call(thisArg[, arg1[, arg2[, ...]]])

該 方法在使用一個(gè)指定的this值和若干個(gè)指定的參數(shù)值的前提下調(diào)用某個(gè)函數(shù)或方法.

參數(shù)

thisArg

在fun函數(shù)運(yùn)行時(shí)指定的this值。需要注意的是,指定的this值并不一定是該函數(shù)執(zhí)行時(shí)真正的this值,如果這個(gè)函數(shù)處于非嚴(yán)格模式下,則指定為null和undefined的this值會(huì)自動(dòng)指向全局對(duì)象(瀏覽器中就是window對(duì)象),同時(shí)值為原始值(數(shù)字,字符串,布爾值)的this會(huì)指向該原始值的自動(dòng)包裝對(duì)象。

arg1, arg2, ...

指定的參數(shù)列表。

當(dāng)調(diào)用一個(gè)函數(shù)時(shí),可以賦值一個(gè)不同的 this 對(duì)象。this 引用當(dāng)前對(duì)象,即 call 方法的第一個(gè)參數(shù)。通過(guò) call 方法,你可以在一個(gè)對(duì)象上借用另一個(gè)對(duì)象上的方法,比如Object.prototype.toString.call([]),就是一個(gè)Array對(duì)象借用了Object對(duì)象上的方法。

作用:

使用call方法調(diào)用父構(gòu)造函數(shù)

使用call方法調(diào)用匿名函數(shù)

使用call方法調(diào)用匿名函數(shù)并且指定上下文的'this'

這里插個(gè)題外話:

apply 與 call() 非常相似,不同之處在于提供參數(shù)的方式。apply 使用參數(shù)數(shù)組而不是一組參數(shù)列表。apply 可以使用數(shù)組字面量(array literal),如 fun.apply(this, ['eat', 'bananas']),或數(shù)組對(duì)象, 如 fun.apply(this, new Array('eat', 'bananas'))。你也可以使用 arguments 對(duì)象作為 argsArray 參數(shù)。 arguments 是一個(gè)函數(shù)的局部變量。 它可以被用作被調(diào)用對(duì)象的所有未指定的參數(shù)。 這樣,你在使用apply函數(shù)的時(shí)候就不需要知道被調(diào)用對(duì)象的所有參數(shù)。 你可以使用arguments來(lái)把所有的參數(shù)傳遞給被調(diào)用對(duì)象。 被調(diào)用對(duì)象接下來(lái)就負(fù)責(zé)處理這些參數(shù)。

從 ECMAScript 第5版開始,可以使用任何種類的類數(shù)組對(duì)象,就是說(shuō)只要有一個(gè) length 屬性和[0...length) 范圍的整數(shù)屬性。例如現(xiàn)在可以使用 NodeList 或一個(gè)自己定義的類似 {'length': 2, '0': 'eat', '1': 'bananas'} 形式的對(duì)象。

call, apply方法區(qū)別是,從第二個(gè)參數(shù)起, call方法參數(shù)將依次傳遞給借用的方法作參數(shù), 而apply 直接將這些參數(shù)放到一個(gè)數(shù)組中再傳遞, 最后借用方法的參數(shù)列表是一樣的.

應(yīng)用場(chǎng)景:當(dāng)參數(shù)明確時(shí)可用call, 當(dāng)參數(shù)不明確時(shí)可用apply給合arguments

現(xiàn)在先給出一個(gè)例子

總所皆知,onscolll,onresize等是非常耗性能,窗口縮放時(shí)打印數(shù)字。

var count = ;

window.onresize = function () {

count++;

console.log(count);

}

在chrome瀏覽器中伸縮瀏覽器窗口大小,打印如下

這顯然不是我們想要的,那如果我們換成ajax請(qǐng)求的話,那么就會(huì)縮放一次窗口會(huì)連續(xù)觸發(fā)多次ajax請(qǐng)求,下面我們?cè)囍褂煤瘮?shù)節(jié)流的操作試試一下;當(dāng)然加個(gè)settimeout()的定時(shí)器就好了,

第一種封裝方法

var count = ;

function oCount() {

count++;

console.log(count);

}

window.onresize = function () {

delayFun(oCount)

};

function delayFun(method, thisArg) {

clearTimeout(method.props);

method.props = setTimeout(function () {

method.call(thisArg)

}, )

}

第二種封裝方法

構(gòu)造一個(gè)閉包,使用閉包的方式形成一個(gè)私有的作用域來(lái)存放定時(shí)器timer, timer是通過(guò)傳參數(shù)的形式引入的。

var count = ;

function oCount() {

count++;

console.log(count);

}

var funs= delayFun(oCount,);

window.onresize = function () {

funs()

};

function delayFun(func, wait) {

var timer = null;

return function () {

var context = this,

args = arguments;

clearTimeout(timer);

timer = setTimeout(function () {

func.apply(context, args);

}, wait)

};

}

對(duì)第二種方法優(yōu)化一下,性能會(huì)更好

這里返回一個(gè)函數(shù),如果它被不間斷地調(diào)用,它將不會(huì)得到執(zhí)行。該函數(shù)在停止調(diào)用 N 毫秒后,再次調(diào)用它才會(huì)得到執(zhí)行。如果有傳遞 ‘immediate' 參數(shù),會(huì)馬上將函數(shù)安排到執(zhí)行隊(duì)列中,而不會(huì)延遲。

function delayFun (func, wait, immediate) {

var timeout;

return function() {

var context = this, args = arguments;

var later = function() {

timeout = null;

if (!immediate) func.apply(context, args);

};

var callNow = immediate && !timeout;

clearTimeout(timeout);

timeout = setTimeout(later, wait);

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

};

};

// 用法

var myEfficientFn = delayFun (function() {

// 所有繁重的操作

}, );

window.addEventListener('resize', myEfficientFn);

函數(shù)不允許回調(diào)函數(shù)在指定時(shí)間內(nèi)執(zhí)行多于一次。當(dāng)為一個(gè)會(huì)頻繁觸發(fā)的事件分配一個(gè)回調(diào)函數(shù)時(shí),該函數(shù)顯得尤為重要。

setTimeout這么厲害,那么我們是可以在項(xiàng)目中大量使用嗎?

我個(gè)人是不建議的,在我們業(yè)務(wù)中,基本上是禁止在業(yè)務(wù)邏輯中使用setTimeout的,因?yàn)槲宜吹降暮芏嗍褂梅绞蕉际且恍﹩?wèn)題好解決,setTimeout作為一個(gè)hack的方式。

例如,當(dāng)一個(gè)實(shí)例還沒有初始化的前,我們就使用這個(gè)實(shí)例,錯(cuò)誤的解決辦法是使用實(shí)例時(shí)加個(gè)setTimeout,確保實(shí)例先初始化。

為什么錯(cuò)誤?這里其實(shí)就是使用hack的手段

第一是埋下了坑,打亂模塊的生命周期

第二是出現(xiàn)問(wèn)題時(shí),setTimeout其實(shí)是很難調(diào)試的。

我認(rèn)為正確的使用方式是,看看生命周期(可參考《關(guān)于軟件的生命周期 》),把實(shí)例化提到使用前執(zhí)行。

有關(guān)JS中setTimeout的巧妙用法前端函數(shù)節(jié)流,小編就給大家介紹到這里,希望對(duì)大家有所幫助!

更多信息請(qǐng)查看網(wǎng)絡(luò)編程
易賢網(wǎng)手機(jī)網(wǎng)站地址:JS中setTimeout的巧妙用法前端函數(shù)節(jié)流
由于各方面情況的不斷調(diào)整與變化,易賢網(wǎng)提供的所有考試信息和咨詢回復(fù)僅供參考,敬請(qǐng)考生以權(quán)威部門公布的正式信息和咨詢?yōu)闇?zhǔn)!

2025國(guó)考·省考課程試聽報(bào)名

  • 報(bào)班類型
  • 姓名
  • 手機(jī)號(hào)
  • 驗(yàn)證碼
關(guān)于我們 | 聯(lián)系我們 | 人才招聘 | 網(wǎng)站聲明 | 網(wǎng)站幫助 | 非正式的簡(jiǎn)要咨詢 | 簡(jiǎn)要咨詢須知 | 新媒體/短視頻平臺(tái) | 手機(jī)站點(diǎn) | 投訴建議
工業(yè)和信息化部備案號(hào):滇ICP備2023014141號(hào)-1 云南省教育廳備案號(hào):云教ICP備0901021 滇公網(wǎng)安備53010202001879號(hào) 人力資源服務(wù)許可證:(云)人服證字(2023)第0102001523號(hào)
云南網(wǎng)警備案專用圖標(biāo)
聯(lián)系電話:0871-65099533/13759567129 獲取招聘考試信息及咨詢關(guān)注公眾號(hào):hfpxwx
咨詢QQ:1093837350(9:00—18:00)版權(quán)所有:易賢網(wǎng)
云南網(wǎng)警報(bào)警專用圖標(biāo)