3 個經常被問到的 JavaScript 面試題

Lud4185 7年前發布 | 15K 次閱讀 JavaScript開發 JavaScript

JavaScript是所有現代瀏覽器的官方語言。 因此,JavaScript 問題出現在各種開發人員的面試中。

本文不是講述最新的JavaScript庫,日常的開發實踐或任何新的 ES6 函數 。 相反,在討論JavaScript時,經常會在面試中出現這3個問題。 我自己被問到過這些問題,我的朋友告訴我他們也被到問過。

當然,你在JavaScript面試前不應該只學習這3個問題 – 這里有 很多 方法 可以 讓你更好地準備即將到來的面試 – 但面試官可能會問到下面是3個問題,來判斷你對JavaScript語言的理解和DOM的掌握程度。

讓我們開始吧!請注意,我們將在下面的示例中使用原生 JavaScript,因為你的面試官通常想看看你在沒有第三方庫(比如jQuery)的幫助下,是如何理解 JavaScript 和 DOM 的。

問題 #1: 事件委托

愚人碼頭注:也叫事件委派,時間代理等;

當構建應用程序時,有時你需要將事件監聽器綁定到頁面上的按鈕,文本或圖像上,以便在用戶與元素交互時執行某些操作。

如果我們以一個簡單的待辦事項列表為例,面試官可能會告訴你,他們希望在用戶單擊其中一個列表項時需要執行某些操作。 他們希望你用 JavaScript 實現這個功能,假設HTML代碼如下:

<ul id="todo-app">
  <li class="item">Walk the dog</li>
  <li class="item">Pay bills</li>
  <li class="item">Make dinner</li>
  <li class="item">Code for one hour</li>
</ul>

你可能會想像下面這樣在元素綁定事件監聽器:

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: 在循環內使用閉包(Closures)

閉包常常在面試中出現,以便面試官衡量你對這門語言的熟悉程度,以及是否知道何時使用閉包。

閉包的本質是 一個內部函數訪問其作用域之外的變量 。閉包可以用于實現諸如 私有變量 和 創建 工廠函數 之類的東西。關于使用閉包的常見面試問題是這樣的:

編寫一個函數,它將循環遍歷整數列表,并在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 的值,其在循環結束時為 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)來提升頁面速度和性能。一個真實的案例,來自 guest post on css-tricks :

在2011年,一個問題在推ter上被提出:當你滾動推ter feed時,它會會變得非常慢甚至未響應。John Resig 就這個問題發布了一篇博文 ,它解釋了直接綁定函數到 scroll 事件上是多么糟糕的事。

函數防抖動(Debouncing) 是解決這個問題的一種方式,通過限制需要經過的時間,直到再次調用函數。一個正確實現函數防抖的方法是:把多個函數放在一個函數里調用,隔一定時間執行一次。這里有一個使用原生JavaScript實現的例子,用到了 作用域 、閉包、 this 和 定時事件 :

// debounce函數用來包裹我們的事件
function debounce(fn, delay) {
  // 持久化一個定時器 timer
  let timer = null;
  // 閉包函數可以訪問 timer
  return function() {
    // 通過 'this' 和 'arguments'
    // 獲得函數的作用域和參數
    let context = this;
    let args = arguments;
    // 如果事件被觸發,清除 timer 并重新開始計時
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  }
}

當這個函數綁定在一個事件上,只有經過一段指定的時間后才會被調用。

你可以像這樣去使用這個函數:

// 當用戶滾動時函數會被調用
function foo() {
  console.log('You are scrolling!');
}

// 在事件觸發的兩秒后,我們包裹在debounce中的函數才會被觸發
let elem = document.getElementById('container');
elem.addEventListener('scroll', debounce(foo, 2000));

函數節流是另一個類似函數防抖的技巧,除了使用等待一段時間再調用函數的方法,函數節流還限制固定時間內只能調用一次。所以一個事件如果在100毫秒內發生10次,函數節流會每2秒調用一次函數,而不是100毫秒內全部調用。

 

 

來自:http://www.css88.com/archives/7059

 

 本文由用戶 Lud4185 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!