摘要:閉包能用來實(shí)現(xiàn)私有化和創(chuàng)建工廠函數(shù)等作用。關(guān)于閉包的常見面試題是這樣的寫一個(gè)函數(shù),循環(huán)一個(gè)整數(shù)數(shù)組,延遲秒打印這個(gè)數(shù)組中每個(gè)元素的索引。
文章來源:http://mp.weixin.qq.com/s/vs0...
前言在公眾號(hào)上看到了這篇文章,覺得很有用,有助于理解JS學(xué)習(xí)中的一些重點(diǎn)難點(diǎn)。決定把它整理下發(fā)布出來。該文章主要介紹了JS中的三個(gè)問題。在以后的幾篇文章里,我會(huì)詳細(xì)介紹這三個(gè)問題。
文章內(nèi)容JavaScript 是所有現(xiàn)代瀏覽器的官方語言。因此,各種語言的開發(fā)者面試中都會(huì)遇到 JavaScript 問題。
本文不講最新的 JavaScript 庫,通用開發(fā)實(shí)踐,或任何新的 ES6 函數(shù)。而是講講面試中經(jīng)常出現(xiàn)的 3 個(gè) JavaScript 問題。我問過這些問題,我的朋友說他們也問。
當(dāng)然不是說你在準(zhǔn)備 JavaScript 面試時(shí)只要學(xué)習(xí)這 3 個(gè)問題 —— 你還有很多途徑去更好的準(zhǔn)備即將到來的面試 —— 但面試官很有可能通過下面 3 個(gè)問題來判斷你了解和掌握 JavaScript 和 DOM 的情況。
讓我們開始吧!注意下面的例子中我們使用原生 JavaScript, 因?yàn)槊嬖嚬偻ǔO肟疾槟阍诓唤柚鷰欤ɡ?jQuery)的幫助時(shí)掌握 JavaScript 和 DOM 的情況。
創(chuàng)建應(yīng)用時(shí),有時(shí)需要給頁面中的按鈕,文字,或圖片添加事件監(jiān)聽器,當(dāng)用戶與這些元素交互時(shí)觸發(fā)某些操作。
我們以一個(gè)簡(jiǎn)單的代辦事項(xiàng)列表為例,面試官會(huì)告訴你,他們希望在用戶點(diǎn)擊列表中某一項(xiàng)時(shí)觸發(fā)一個(gè)動(dòng)作。并讓你用 JavaScript 根據(jù)下面的 HTML 代碼實(shí)現(xiàn)這個(gè)功能:
你可能會(huì)像下面的代碼一樣給元素添加事件監(jiān)聽器:
document.addEventListener("DOMContentLoaded", function() { let app = document.getElementById("todo-app"); let items = app.getElementsByClassName("item"); // 給每個(gè)列表項(xiàng)添加事件監(jiān)聽器 for (let item of items) { item.addEventListener("click", function() { alert("you clicked on item: " + item.innerHTML); }); } });
當(dāng)然上面的代碼能完成面試官的需求,問題是每個(gè)列表項(xiàng)都會(huì)加上一個(gè)事件監(jiān)聽器。當(dāng)列表只有 4 項(xiàng)時(shí)沒有問題,但如果有人給代辦事項(xiàng)列表新增了 10,000 個(gè)事項(xiàng)呢(他們也許有一大堆事情要做)?那時(shí)函數(shù)會(huì)創(chuàng)建 10,000 個(gè)事件監(jiān)聽器,然后把它們都添加到 DOM 上。這樣效率非常低。
面試中最好首先問一下面試官用戶最多可以添加多少個(gè)代辦事項(xiàng)。如果永遠(yuǎn)不會(huì)超過 10 個(gè),那上面的代碼運(yùn)行起來就沒有問題。但如果用戶輸入待辦事項(xiàng)的數(shù)量沒有上限,那你就得換一個(gè)更高效的解決方案。
如果應(yīng)用有上百個(gè)事件監(jiān)聽器,更高效的解決方案是給最外層的容器添加一個(gè)事件監(jiān)聽器,當(dāng)用戶真正點(diǎn)擊的時(shí)候再去獲取實(shí)際被點(diǎn)擊的代辦事項(xiàng)。這被稱為事件代理,這比給每個(gè)代辦事項(xiàng)都多帶帶添加事件監(jiān)聽器更高效。
下面是事件代理的代碼:
document.addEventListener("DOMContentLoaded", function() { let app = document.getElementById("todo-app"); // 給容器添加事件監(jiān)聽器 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)中使用閉包
面試中經(jīng)常會(huì)問到閉包,因?yàn)槊嬖嚬倌芡ㄟ^這個(gè)問題的回答判斷你對(duì)語言的熟悉程度,以及考察你是否知道什么時(shí)候使用閉包。
閉包就是能訪問作用域外部變量的內(nèi)部函數(shù) 。閉包能用來實(shí)現(xiàn)私有化和創(chuàng)建工廠函數(shù)等作用。關(guān)于閉包的常見面試題是這樣的:
寫一個(gè)函數(shù),循環(huán)一個(gè)整數(shù)數(shù)組,延遲 3 秒打印這個(gè)數(shù)組中每個(gè)元素的索引。
這個(gè)問題常見(不正確)的實(shí)現(xiàn)是這樣:
const arr = [10, 12, 15, 21];for (var i = 0; i < arr.length; i++) { setTimeout(function() { console.log("The index of this number is: " + i); }, 3000); }
如果你運(yùn)行這段函數(shù),你會(huì)發(fā)現(xiàn) 3 秒之后每次都打印的是 4,而不是預(yù)期的 0, 1, 2, 3。
為了正確的找到出現(xiàn)這種情況的原因,你需要理解 JavaScript 是如何運(yùn)行這段代碼的,這也是面試官想要考察你的地方。
原因是 setTimeout 函數(shù)創(chuàng)建了一個(gè)訪問外部作用域的函數(shù)(閉包),就是包含索引 i 的那個(gè)循環(huán)。3 秒之后,函數(shù)開始執(zhí)行打印 i 的值,而此時(shí)循環(huán)也結(jié)束了,i 的值已經(jīng)是 4。因?yàn)檠h(huán)遍歷 0, 1, 2, 3, 4 后最終停在了 4。
實(shí)際上有好幾種方法能正確解決這個(gè)問題。這里有兩個(gè):
const arr = [10, 12, 15, 21];for (var i = 0; i < arr.length; i++) { // 給每個(gè)函數(shù)傳入變量 i 讓其能訪問正確的索引 setTimeout(function(i_local) { return function() { console.log("The index of this number is: " + i_local); } }(i), 3000); } const arr = [10, 12, 15, 21];for (let i = 0; i < arr.length; i++) { // 使用 ES6 中的 let 關(guān)鍵字,它會(huì)在函數(shù)調(diào)用時(shí)創(chuàng)建一個(gè)新的綁定 // 了解更多:http://exploringjs.com/es6/ch_variables.html#sec_let-const-loop-heads setTimeout(function() { console.log("The index of this number is: " + i); }, 3000); }問題 #3: Debouncing(防抖動(dòng))
有些瀏覽器事件能在很短的時(shí)間內(nèi)被觸發(fā)多次,例如調(diào)整窗口大小或滾動(dòng)頁面。如果你給窗口滾動(dòng)事件添加一個(gè)事件監(jiān)聽器,然后用戶不停地快速向下滾動(dòng)頁面,那你的事件可能在 3 秒之內(nèi)被觸發(fā)數(shù)千次。這會(huì)導(dǎo)致非常嚴(yán)重的性能問題。
如果在面試中討論到構(gòu)建應(yīng)用程序,以及滾動(dòng)事件,窗口調(diào)整事件,或者鍵盤事件等,請(qǐng)務(wù)必提及 debouncing 或者 throttling,作為提高頁面速度與性能的方法。來一個(gè) css-tricks 的實(shí)例:
2011 年,Twitter 出了一個(gè)問題:當(dāng)滾動(dòng) Twitter 摘要時(shí),頁面變的很卡甚至無響應(yīng)。John Resig 寫了一篇關(guān)于這個(gè)問題的博客,解釋了直接將耗時(shí)的函數(shù)綁定在 scroll 事件上是一個(gè)多么糟糕的想法。
Debouncing 是解決這個(gè)問題的一種方法,它的做法是限制下次函數(shù)調(diào)用之前必須等待的時(shí)間間隔。正確實(shí)現(xiàn) debouncing 的方法是將若干個(gè)函數(shù)調(diào)用 合成 一次,并在給定時(shí)間過去之后僅被調(diào)用一次。下面是一個(gè)原生 JavaScript 的實(shí)現(xiàn),用到了作用域, 閉包, this, 和 計(jì)時(shí)事件:
// 將會(huì)包裝事件的 debounce 函數(shù) function debounce(fn, delay) { // 維護(hù)一個(gè) timer let timer = null; // 能訪問 timer 的閉包 return function() { // 通過 ‘this’ 和 ‘a(chǎn)rguments’ 獲取函數(shù)的作用域和變量 let context = this; let args = arguments; // 如果事件被調(diào)用,清除 timer 然后重新設(shè)置 timer clearTimeout(timer); timer = setTimeout(function() { fn.apply(context, args); }, delay); } }
這個(gè)函數(shù) — 當(dāng)傳入一個(gè)事件(fn)時(shí) — 會(huì)在經(jīng)過給定的時(shí)間(delay)后執(zhí)行。
函數(shù)這樣用:
// 當(dāng)用戶滾動(dòng)時(shí)被調(diào)用的函數(shù) function foo() { console.log("You are scrolling!"); } // 在 debounce 中包裝我們的函數(shù),過 2 秒觸發(fā)一次 let elem = document.getElementById("container"); elem.addEventListener("scroll", debounce(foo, 2000));
Throttling 是與 debouncing 類似的一種技術(shù),但它不是在調(diào)用函數(shù)之前等待一段時(shí)間,throttling 是在較長(zhǎng)的時(shí)間間隔內(nèi)調(diào)用函數(shù)。所以如果一個(gè)事件每 100 毫秒被觸發(fā) 10 次,throttling 會(huì)在每隔 2 秒時(shí)執(zhí)行一次這個(gè)函數(shù),而不是在 100 毫秒內(nèi)執(zhí)行 10 次事件。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/82029.html
摘要:將指定的數(shù)字索引值轉(zhuǎn)換成字符串索引值變成,然后將其作為屬性名來用。返回一個(gè)由刪除元素組成的數(shù)組。該方法返回的數(shù)組元素是調(diào)用的數(shù)組的一個(gè)子集。使用的函數(shù)有四個(gè)參數(shù)初始值積累值數(shù)組元素元素索引數(shù)組本身。 前言 很多人在學(xué)習(xí)原生JS的過程中會(huì)遇到一些疑惑,比如在學(xué)習(xí)array時(shí),就很容易搞不清哪些方法會(huì)改變?cè)瓉頂?shù)組,哪些方法不會(huì)改變?cè)瓉頂?shù)組?再比如很多人會(huì)使用new Date()獲取時(shí)間,卻...
摘要:如果你想讓一個(gè)數(shù)組元素的值變?yōu)槎皇莿h除它,可以使用給其賦值而不是使用操作符。此時(shí)數(shù)組元素是在數(shù)組中的操作符與直接釋放內(nèi)存只能通過解除引用來間接釋放沒有關(guān)系。 delete 操作符用來刪除一個(gè)對(duì)象的屬性 語法EDIT delete expression expression 應(yīng)該是一個(gè)對(duì)象的屬性引用,例如: delete object.property delete object[p...
摘要:一些知識(shí)點(diǎn)有哪些方法方法前端從入門菜鳥到實(shí)踐老司機(jī)所需要的資料與指南合集前端掘金前端從入門菜鳥到實(shí)踐老司機(jī)所需要的資料與指南合集歸屬于筆者的前端入門與最佳實(shí)踐。 工欲善其事必先利其器-前端實(shí)習(xí)簡(jiǎn)歷篇 - 掘金 有幸認(rèn)識(shí)很多在大廠工作的學(xué)長(zhǎng),在春招正式開始前為我提供很多內(nèi)部推薦的機(jī)會(huì),非常感謝他們對(duì)我的幫助。現(xiàn)在就要去北京了,對(duì)第一份正式的實(shí)習(xí)工作也充滿期待,也希望把自己遇到的一些問題和...
摘要:詳解十大常用設(shè)計(jì)模式力薦深度好文深入理解大設(shè)計(jì)模式收集各種疑難雜癥的問題集錦關(guān)于,工作和學(xué)習(xí)過程中遇到過許多問題,也解答過許多別人的問題。介紹了的內(nèi)存管理。 延遲加載 (Lazyload) 三種實(shí)現(xiàn)方式 延遲加載也稱為惰性加載,即在長(zhǎng)網(wǎng)頁中延遲加載圖像。用戶滾動(dòng)到它們之前,視口外的圖像不會(huì)加載。本文詳細(xì)介紹了三種延遲加載的實(shí)現(xiàn)方式。 詳解 Javascript十大常用設(shè)計(jì)模式 力薦~ ...
摘要:反之亦然非嚴(yán)格合并嚴(yán)格看起來是非嚴(yán)格的。在普通的里面給一個(gè)拼寫錯(cuò)誤的變量名賦值會(huì)使全局對(duì)象新增一個(gè)屬性并繼續(xù)工作盡管后面可能出錯(cuò)在現(xiàn)在的中有可能。第三嚴(yán)格模式禁止刪除聲明變量。 文章整理自MSDN:https://developer.mozilla.org... 1.逐步使用嚴(yán)格模式 ECMAScript 5的嚴(yán)格模式是JavaScript中的一種限制性更強(qiáng)的變種方式。嚴(yán)格模式不是一個(gè)...
閱讀 3301·2021-10-11 11:08
閱讀 4441·2021-09-22 15:54
閱讀 922·2019-08-30 15:56
閱讀 877·2019-08-30 15:55
閱讀 3550·2019-08-30 15:52
閱讀 1360·2019-08-30 15:43
閱讀 1944·2019-08-30 11:14
閱讀 2514·2019-08-29 16:11