摘要:隆重請出主角防抖與節(jié)流。防抖與節(jié)流的異同相同都是防止某一時間段內,函數被頻繁調用執(zhí)行,通過時間頻率控制,減少回調函數執(zhí)行次數,來實現相關性能優(yōu)化。參考文章分鐘理解的節(jié)流防抖及使用場景函數防抖和節(jié)流
本篇課題,或許早已是爛大街的解讀文章。不過春招系列面試下來,不少伙伴們還是似懂非懂地栽倒在(~面試官~)深意的笑容之下,權當溫故知新。
JavaScript的執(zhí)行過程,是基于棧來進行的。復雜的程序代碼被封裝到函數中,程序執(zhí)行時,函數不斷被推入執(zhí)行棧中。所以 "執(zhí)行棧" 也稱 "函數執(zhí)行棧"。
函數中封裝的代碼塊,一般都有相對復雜的邏輯處理(計算/判斷),例如函數中可能會涉及到 DOM 的渲染更新,復雜的計算與驗證, Ajax 數據請求等等。
前端頁面的操作權,大部分都是屬于瀏覽端的客戶爸爸們(單身三十年的手速,惹不起惹不起?。。。?。如果函數被頻繁調用,造成的性能開銷絕對不只一點點。
前: DOM 頻繁重繪的卡頓讓客戶爸爸們想把你揪出來一頓大招。。。
后: 后端同學正在提刀趕來的路上:“為什么我的接口被你玩掛了”。。。
既要提升用戶體驗,又要減少后端服務開銷,可見我們大前端的使命不只一頁PPT。說好前因,接著就是后果了。既然有優(yōu)化的需求,必然就要有相應的解決方案。隆重請出主角: “防抖” 與 “節(jié)流”。
防抖(debounce)在事件被觸發(fā) n 秒后再執(zhí)行回調函數,如果在這 n 秒內又被觸發(fā),則重新計時延遲時間。
生活化理解:英雄的技能條,技能條讀完才能使用技能(R大招60s)
防抖的實現方式分兩種 “立即執(zhí)行” 和 “非立即執(zhí)行”,區(qū)別在于第一次觸發(fā)時,是否立即執(zhí)行回調函數。
非立即執(zhí)行”非立即執(zhí)行防抖“ 指事件觸發(fā)后,回調函數不會立即執(zhí)行,會在延遲時間 n 秒后執(zhí)行,如果 n 秒內被調用多次,則重新計時延遲時間
// e.g. 防抖 - 非立即執(zhí)行 function debounce(func, delay) { var timeout; return function() { var context = this; var args = arguments; // && 短路運算 == if(timeout) else {...} timeout && clearTimeout(timeout); timeout = setTimeout(function(){ func.apply(context, args); }, delay); } } // 調用 var printUserName = debounce(function(){ console.log(this.value); }, 800); document.getElementById("username") .addEventListener("keyup", printUserName);立即執(zhí)行
“立即執(zhí)行防抖” 指事件觸發(fā)后,回調函數會立即執(zhí)行,之后要想觸發(fā)執(zhí)行回調函數,需等待 n 秒延遲
// e.g. 防抖 - 立即執(zhí)行 function debounce(func, delay) { var timeout; return function() { var context = this; var args = arguments; callNow = !timeout; timeout = setTimeout(function() { timeout = null; }, delay); callNow && func.apply(context, args); } }
函數防抖原理:通過維護一個定時器,其延遲計時以最后一次觸發(fā)為計時起點,到達延遲時間后才會觸發(fā)函數執(zhí)行。
節(jié)流(throttle)規(guī)定在一個單位時間內,只能觸發(fā)一次函數。如果這個單位時間內觸發(fā)多次函數,只有一次生效(間隔執(zhí)行)
生活化理解:
FPS射擊游戲子彈射速(即使按住鼠標左鍵,射出子彈的速度也是限定的)
水龍頭的滴水(水滴攢到一定重量才會下落)
函數節(jié)流實現的方式有 “時間戳” 和 “定時器” 兩種。
時間戳// e.g. 節(jié)流 - 時間戳 function throttle(func, delay) { var lastTime = 0; return function() { var context = this; var args = arguments; var nowTime = +new Date(); if (nowTime > lastTime + delay) { func.apply(context, args) lastTime = nowTime; } } }
“時間戳” 的方式,函數在時間段開始時執(zhí)行。
缺點:假定函數間隔1s執(zhí)行,如果最后一次停止觸發(fā),卡在4.2s,則不會再執(zhí)行。
定時器// e.g. 節(jié)流 - 定時器 function throttle(func, delay) { var timeout; return function() { var context = this; var args = arguments; if (!timeout) { setTimeout(function(){ func.apply(context, args); timeout = null; }, delay) } } }
“定時器” 的方式,函數在時間段結束時執(zhí)行??衫斫鉃楹瘮挡⒉粫⒓磮?zhí)行,而是等待延遲計時完成才執(zhí)行。(由于定時器延時,最后一次觸發(fā)后,可能會再執(zhí)行一次回調函數)
時間戳 + 定時器(互補優(yōu)化)// e.g. 節(jié)流 - 時間戳 + 定時器 function throttle(func, delay) { let lastTime, timeout; return function() { let context = this; let args = arguments; let nowTime = +new Date(); if (lastTime && nowTime < lastTime + delay) { timeout && clearTimeout(timeout); timeout = setTimeout(function(){ lastTime = nowTime; func.apply(context, args); }, delay); } else { lastTime = nowTime; func.apply(context, args); } } }
合并優(yōu)化的原理:“時間戳”方式讓函數在時間段開始時執(zhí)行(第一次觸發(fā)立即執(zhí)行),“定時器”方式讓函數在最后一次事件觸發(fā)后(如4.2s)也能觸發(fā)。
函數節(jié)流原理:一定時間內只觸發(fā)一次,間隔執(zhí)行。通過判斷是否到達指定觸發(fā)時間,間隔時間固定。
“防抖” 與 “節(jié)流” 的異同相同:都是防止某一時間段內,函數被頻繁調用執(zhí)行,通過時間頻率控制,減少回調函數執(zhí)行次數,來實現相關性能優(yōu)化。
區(qū)別:“防抖”是某一時間內只執(zhí)行一次,最后一次觸發(fā)后過段時間執(zhí)行,而“節(jié)流”則是間隔時間執(zhí)行,間隔時間固定。
“防抖” 與 “節(jié)流” 的應用場景 防抖文本輸入搜索聯想
文本輸入驗證(包括 Ajax 后端驗證)
節(jié)流鼠標點擊
監(jiān)聽滾動 scroll
窗口 resize
mousemove 拖拽
應用場景還有很多,具體場景需具體分析。只要涉及高頻的函數調用,都可參考函數防抖節(jié)流的優(yōu)化方案。
鼓起勇氣寫在結尾:以上代碼都不是 “完美” 的 “防抖 / 節(jié)流” 實現代碼!?。H就實現方式和基本原理,淺談分解一二。
實際代碼開發(fā)中,一般會引入lodash 相對 “靠譜” 的第三方庫,幫我們去實現防抖節(jié)流的工具函數。有興趣的伙伴們可閱讀 lodash 相關源碼,加深印象理解可再讀以下參考文章。
參考文章
7分鐘理解JS的節(jié)流、防抖及使用場景
函數防抖和節(jié)流
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://systransis.cn/yun/102343.html
摘要:封裝方法也比較簡單,書中對此問題也進行了處理使用定時器,讓函數延遲秒后執(zhí)行,在此秒內,然后函數再次被調用,則刪除上次的定時器,取消上次調用的隊列任務,重新設置定時器。 在實際開發(fā)中,函數一定是最實用最頻繁的一部分,無論是以函數為核心的函數式編程,還是更多人選擇的面向對象式的編程,都會有函數的身影,所以對函數進行深入的研究是非常有必要的。 函數節(jié)流 比較直白的說,函數節(jié)流就是強制規(guī)定一...
摘要:文章來源詳談防抖和節(jié)流輕松理解函數節(jié)流和函數防抖函數防抖和節(jié)流好啦,今天的小菊花課堂之的防抖與節(jié)流的內容就告一段落啦,感各位能耐心看到這里。 前言 陸游有一首《冬夜讀書示子聿》——古人學問無遺力,少壯工夫老始成。紙上得來終覺淺,絕知此事要躬行。,其中的意思想必大家都能明白,在學習或工作中,不斷的印證著這首詩的內涵。所以,又有了此篇小菊花文章。 詳解 在前端開發(fā)中,我們經常會碰到一些會持...
摘要:文章來源詳談防抖和節(jié)流輕松理解函數節(jié)流和函數防抖函數防抖和節(jié)流好啦,今天的小菊花課堂之的防抖與節(jié)流的內容就告一段落啦,感各位能耐心看到這里。 前言 陸游有一首《冬夜讀書示子聿》——古人學問無遺力,少壯工夫老始成。紙上得來終覺淺,絕知此事要躬行。,其中的意思想必大家都能明白,在學習或工作中,不斷的印證著這首詩的內涵。所以,又有了此篇小菊花文章。 詳解 在前端開發(fā)中,我們經常會碰到一些會持...
摘要:函數柯里化是把支持多個參數的函數變成接收單一參數的函數,并返回一個函數能接收處理剩余參數,而反柯里化就是把參數全部釋放出來。但在一些復雜的業(yè)務邏輯封裝中,函數柯里化能夠為我們提供更好的應對方案,讓我們的函數更具自由度和靈活性。 showImg(https://segmentfault.com/img/bVburN1?w=800&h=600); 柯里化(Curring, 以邏輯學家Has...
摘要:對象是無法通過這種方式深拷貝。這就是函數防抖和節(jié)流要做的事情。函數防抖當觸發(fā)頻率過高時函數基本停止執(zhí)行而函數節(jié)流則是按照一定的頻率執(zhí)行事件。 對象的深淺拷貝 對象的深拷貝與淺拷貝的區(qū)別: 淺拷貝:僅僅復制對象的引用, 而不是對象本身。 深拷貝:把復制的對象所引用的全部對象都復制一遍 淺拷貝的實現: var obj = { age : 18, person : { ...
閱讀 904·2023-04-26 03:03
閱讀 2222·2021-10-12 10:12
閱讀 1217·2021-09-24 09:48
閱讀 1669·2021-09-22 15:25
閱讀 3349·2021-09-22 15:15
閱讀 939·2019-08-29 16:21
閱讀 1082·2019-08-28 18:00
閱讀 3442·2019-08-26 13:44