3 個在 JavaScript 面試前應該知道的問題

簡評:在這篇文章中,作者總結了 3 個在 JavaScript 面試問題中問得最多的問題(不清楚國內是不是)。這三個問題不是關于任何庫的用法或 ES6 的新功能,而主要是對原生 JavaScript 的理解。

Question #1: 事件代理

當創建一個應用時,不可避免的會遇到監聽事件觸發的需求。這里有一個小的簡單的待辦列表要完成,想要在用戶點擊其中一個列表項時觸發一個動作。下面是一段 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');
  
  // attach event listener to each item
  for (let item of items) {
    item.addEventListener('click', function() {
      alert('you clicked on item: ' + item.innerHTML);
    });
  }
});

當然這樣寫在技術上是完全可以的,唯一的問題就是當列表項過多的時候(比如 10,000 個),你的這段函數就將會同時創建 10,000 個監聽函數。

下面是一種更有效率的寫法:

document.addEventListener('DOMContentLoaded', function() {
  let app = document.getElementById('todo-app');
  
  // attach event listener to whole container
  app.addEventListener('click', function(e) {
    if (e.target && e.target.nodeName === 'LI') {
      let item = e.target;
      alert('you clicked on item: ' + item.innerHTML);
    }
  });
});

Question #2: 在循環中使用閉包

閉包是 JavaScript 的一個重要特性,開發者可以用來模擬私有方法。在這里有個簡單的問題:

實現一個函數,循環遍歷整數列表,并在 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);
}

如果你執行這段代碼,會發現每次輸出的是 4 而不是按順序的 0,1,2,3。

原因在于 setTimeout 創建了一個匿名函數并訪問處于外部的變量 i,都處于同一環境中。當 console.log 被調用的時候,匿名函數保持對外部變量 i 的引用,此時 for 循環已經結束, i 的值被修改成了 4。為了得到想要的結果,需要在每次循環中創建變量 i 的拷貝。

事實上正確的寫法有好幾種,這里列舉用得最多的兩種:

const arr = [10, 12, 15, 21];
for (var i = 0; i < arr.length; i++) {
  // pass in the variable i so that each function 
  // has access to the correct index
  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++) {
  // using the ES6 let syntax, it creates a new binding
  // every single time the function is called
  // read more here: http://exploringjs.com/es6/ch_variables.html#sec_let-const-loop-heads
  setTimeout(function() {
    console.log('The index of this number is: ' + i);
  }, 3000);
}

Question #3: Debouncing

有一些瀏覽器事件可以在很短的時間內快速啟動多次,例如調整窗口大小或滾動頁面。如果你在窗口滾動上綁定了事件,那么可能在用戶滾動頁面的幾秒鐘里,你的事件方法就執行了數千次,這就會導致很嚴重的性能問題。

一個真實的案例就是 2011 年 Twitter,在你滾動 Twitter feed 時,其會變得非常慢甚至未響應。這里有一篇 blog 就詳細講了當時的這個 bug,也就是在 scroll 事件上綁定一個復雜函數是多糟的主意。

Debouncing 就是解決這個問題的一種方法,簡單來說就是限制函數調用的間隔時間。如果在時間間隔內再次觸發事件,就重啟定時器并忽略掉這次事件。

// debounce function that will wrap our event
function debounce(fn, delay) {
  // maintain a timer
  let timer = null;
  // closure function that has access to timer
  return function() {
    // get the scope and parameters of the function 
    // via 'this' and 'arguments'
    let context = this;
    let args = arguments;
    // if event is called, clear the timer and start over
    clearTimeout(timer);
    timer = setTimeout(function() {
      fn.apply(context, args);
    }, delay);
  }
}

使用:

// function to be called when user scrolls
function foo() {
  console.log('You are scrolling!');
}

// wrap our function in a debounce to fire once 2 seconds have gone by
let elem = document.getElementById('container');
elem.addEventListener('scroll', debounce(foo, 2000));

與 Debouncing 類似的技術是 Throttling,同樣也是使用計時器來控制事件的觸發,不同之處在于 Throttling 沒有忽略掉事件,而是延遲觸發。如果想了解更多,可以進一步閱讀下面的這幾篇文章。

擴展閱讀:

原文:3 JavaScript questions to watch out for during coding interviews
歡迎關注知乎專欄「極光日報」,每天為 Makers 導讀三篇優質英文文章。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 原文: https://github.com/ecomfe/spec/blob/master/javascript...
    zock閱讀 3,408評論 2 36
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,767評論 18 399
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,466評論 25 708
  • 許多朋友對于【品牌展示】模組中的相關圖片是從哪里設置的不知道。 如要修改【品牌展示模組】中的圖片,路徑為:【系統設...
    wqching閱讀 290評論 0 0
  • 云對天空說 我變成各種樣子 都是為你 只要不離開你就好 不然我會下雨 天空對云說 你是什么樣子 我都不介意 只要你...
    抽煙喝酒燙頭閱讀 129評論 0 0