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