摘要:更高效的解決方案是將一個(gè)事件偵聽器實(shí)際綁定到父容器上,然后在實(shí)際單擊時(shí)可以訪問每個(gè)確切元素。如果將事件偵聽器綁定到窗口滾動(dòng)事件上,并且用戶快速滾動(dòng)頁面,事件很可能會(huì)在短時(shí)間多次觸發(fā)。
原文鏈接
問題 #1: 事件委托事件委托,也叫事件委派,事件代理。
當(dāng)構(gòu)建應(yīng)用程序時(shí),有時(shí)需要將事件監(jiān)聽器綁定到頁面上的某些元素上,以便在用戶與元素交互時(shí)執(zhí)行某些操作。
假設(shè)我們現(xiàn)在有一個(gè)無序列表:
我們需要在上綁定點(diǎn)擊事件,我們可能會(huì)這樣操作:
app = document.getElementById("todo-app"); let items = app.getElementsByClassName("item"); // 將事件偵聽器綁定到每個(gè)列表項(xiàng) for (let item of items) { item.addEventListener("click", function() { alert("you clicked on item: " + item.innerHTML); }); }
雖然這樣可以實(shí)現(xiàn)功能,但問題是要多帶帶將事件偵聽器綁定到每個(gè)列表項(xiàng)。這是4個(gè)元素,沒什么大問題,但如果列表中有10,000個(gè)事項(xiàng),怎么辦?這個(gè)函數(shù)將會(huì)創(chuàng)建10,000個(gè)獨(dú)立的事件監(jiān)聽器,并將每個(gè)事件監(jiān)聽器綁定到 DOM 。這樣代碼執(zhí)行的效率非常低下。
更高效的解決方案是將一個(gè)事件偵聽器實(shí)際綁定到父容器上,然后在實(shí)際單擊時(shí)可以訪問每個(gè)確切元素。這被稱為事件委托,并且它比每個(gè)元素多帶帶綁定事件的處理程序更高效。
那么上面的代碼可以改變?yōu)椋?/p>
let app = document.getElementById("todo-app"); // 事件偵聽器綁定到整個(gè)容器上 app.addEventListener("click", function(e) { if (e.target && e.target.nodeName === "LI") { let item = e.target; alert("you clicked on item: " + item.innerHTML); } });問題 #2: 在循環(huán)內(nèi)使用閉包(Closures)
閉包的本質(zhì)是一個(gè)內(nèi)部函數(shù)訪問其作用域之外的變量。閉包可以用于實(shí)現(xiàn)諸如 私有變量 和 創(chuàng)建工廠函數(shù)之類的東西。
在面試中我們可能會(huì)見到一段這樣的代碼:
for (var i = 0; i < 4; i++) { setTimeout(function() { console.log(i); }, 1000); }
運(yùn)行上面的代碼控制臺會(huì)在1秒后打印4個(gè)4,而不是0,1,2,3。
其原因是因?yàn)?b>setTimeout函數(shù)創(chuàng)建了一個(gè)可以訪問其外部作用域的函數(shù)(也就是我們經(jīng)常說的閉包),每個(gè)循環(huán)都包含了索引i。
1秒后,該函數(shù)被執(zhí)行并且打印出i的值,其在循環(huán)結(jié)束時(shí)為4,因?yàn)樗难h(huán)周期經(jīng)歷了0,1,2,3,4,并且循環(huán)最終在4時(shí)停止。
下面列舉兩種方案解決這個(gè)問題:
for (var i = 0; i < 4; i++) { // 通過傳遞變量 i // 在每個(gè)函數(shù)中都可以獲取到正確的索引 setTimeout(function(j) { return function() { console.log(j); } }(i), 1000); }
for (let i = 0; i < 4; i++) { // 使用ES6的let語法,它會(huì)創(chuàng)建一個(gè)新的綁定 // 每個(gè)方法都是被多帶帶調(diào)用的 setTimeout(function() { console.log(i); }, 1000); }問題 #3: 函數(shù)防抖(Debouncing)
有一些瀏覽器事件可以在很短的時(shí)間內(nèi)快速啟動(dòng)多次,例如頁面滾動(dòng)事件。如果將事件偵聽器綁定到窗口滾動(dòng)事件上,并且用戶快速滾動(dòng)頁面,事件很可能會(huì)在短時(shí)間多次觸發(fā)。這可能會(huì)導(dǎo)致一些嚴(yán)重的性能問題。
因此,在偵聽滾動(dòng),窗口調(diào)整大小,或鍵盤按下的事件時(shí),請務(wù)必使用函數(shù)防抖動(dòng)(Debouncing)或函數(shù)節(jié)流(Throttling)來提升頁面速度和性能。
函數(shù)防抖(Debouncing)是解決這個(gè)問題的一種方式,通過限制需要經(jīng)過的時(shí)間,直到再次調(diào)用函數(shù)。一個(gè)實(shí)現(xiàn)函數(shù)防抖的方法是:把多個(gè)函數(shù)放在一個(gè)函數(shù)里調(diào)用,隔一定時(shí)間執(zhí)行一次。
這里有一個(gè)使用原生JavaScript實(shí)現(xiàn)的例子,用到了作用域、閉包、this和定時(shí)事件:
function debounce(fn, delay) { // 持久化一個(gè)定時(shí)器 timer let timer = null; // 閉包函數(shù)可以訪問 timer return function() { // 通過 "this" 和 "arguments" 獲得函數(shù)的作用域和參數(shù) let self = this; let args = arguments; // 如果事件被觸發(fā),清除 timer 并重新開始計(jì)時(shí) clearTimeout(timer); timer = setTimeout(function() { fn.apply(self, args); }, delay); } } // 當(dāng)用戶滾動(dòng)時(shí)調(diào)用函數(shù)foo() function foo() { console.log("You are scrolling!"); } // 在事件觸發(fā)的兩秒后,包裹在debounce()中的函數(shù)才會(huì)被觸發(fā) window.addEventListener("scroll", debounce(foo, 2000));
函數(shù)節(jié)流是另一個(gè)類似函數(shù)防抖的技巧,除了使用等待一段時(shí)間再調(diào)用函數(shù)的方法,函數(shù)節(jié)流還限制固定時(shí)間內(nèi)只能調(diào)用一次。所以,如果一個(gè)事件在100毫秒內(nèi)發(fā)生10次,函數(shù)節(jié)流會(huì)每2秒調(diào)用一次函數(shù),而不是100毫秒內(nèi)全部調(diào)用。
(完)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/88170.html
摘要:相反,在討論時(shí),面試中通常會(huì)提到三件事。而認(rèn)為最后一個(gè)參賽者說了算,只要還能吃的,就重新設(shè)定新的定時(shí)器。試想,如果用戶的操作十分頻繁他每次都不等設(shè)置的時(shí)間結(jié)束就進(jìn)行下一次操作,于是每次都為該用戶重新生成定時(shí)器,回調(diào)函數(shù)被延遲了不計(jì)其數(shù)次。本文不是討論最新的 JavaScript 庫、常見的開發(fā)實(shí)踐或任何新的 ES6 函數(shù)。相反,在討論 JavaScript 時(shí),面試中通常會(huì)提到三件事。我自己...
摘要:相反,在討論時(shí),面試中通常會(huì)提到三件事。通過對事件對應(yīng)的回調(diào)函數(shù)進(jìn)行包裹以自由變量的形式緩存時(shí)間信息,最后用來控制事件的觸發(fā)頻率。而認(rèn)為最后一個(gè)參賽者說了算,只要還能吃的,就重新設(shè)定新的定時(shí)器。 showImg(https://segmentfault.com/img/bVboH5x?w=1000&h=750); 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 本...
摘要:可以在該鉤子中進(jìn)一步地更改狀態(tài),不會(huì)觸發(fā)附加的重渲染過程。我工作中只用到,對和不怎么熟與的區(qū)別相同點(diǎn)都支持指令內(nèi)置指令和自定義指令都支持過濾器內(nèi)置過濾器和自定義過濾器都支持雙向數(shù)據(jù)綁定都不支持低端瀏覽器。 看看面試題,只是為了查漏補(bǔ)缺,看看自己那些方面還不懂。切記不要以為背了面試題,就萬事大吉了,最好是理解背后的原理,這樣面試的時(shí)候才能侃侃而談。不然,稍微有水平的面試官一看就能看出,是...
摘要:作者今年大三,在春招過程中參加了多家大公司的面試后,拿到了騰訊的前端實(shí)習(xí),在這里做一些總結(jié),希望給還未參加過實(shí)習(xí)面試的同學(xué)一些幫助。在之后的面試時(shí)就更加從容一些了。 作者今年大三,在春招過程中參加了多家大公司的面試后,拿到了騰訊的前端實(shí)習(xí) offer,在這里做一些總結(jié),希望給還未參加過實(shí)習(xí)面試的同學(xué)一些幫助。 一、簡歷的準(zhǔn)備 簡歷制作是很重要的一個(gè)環(huán)節(jié),一份好的簡歷會(huì)給面試官留下很不錯(cuò)...
摘要:我覺得了解簡歷和面試的技巧可以幫助你更好的去學(xué)習(xí)重要的知識點(diǎn)以及更好地去準(zhǔn)備面試以及面試,說實(shí)話,我個(gè)人覺得這些東西還挺重要的。在本文里,我將介紹我這段時(shí)間里更新簡歷和面試的相關(guān)經(jīng)歷。 分享一篇很不錯(cuò)的文章!本文作者曾經(jīng)寫過《Java Web輕量級開發(fā)面試教程》和 《Java核心技術(shù)及面試指南》這兩本書。我覺得了解簡歷和面試的技巧可以幫助你更好的去學(xué)習(xí)重要的知識點(diǎn)以及更好地去準(zhǔn)備面試以...
閱讀 2418·2021-11-16 11:44
閱讀 852·2021-09-10 11:16
閱讀 2228·2019-08-30 15:54
閱讀 1058·2019-08-30 15:53
閱讀 1901·2019-08-30 13:00
閱讀 621·2019-08-29 17:07
閱讀 3514·2019-08-29 16:39
閱讀 3138·2019-08-29 13:30