前端JS面試題彙總 Part 1(事件委託/this關鍵字/原型鏈/AMD與CommonJS/自執行函式)

大魔王薩格拉斯發表於2018-03-01

原文:https://github.com/yangshun/front-end-interview-handbook/blob/master/questions/javascript-questions.md

最近將持續翻譯JavaScript面試題,希望對各位有所幫助。  

(文章中斜體字部分為譯者新增)

 

目錄:

Part 1(事件委託/this關鍵字/原型鏈/AMD與CommonJS/自執行函式)

Part 2 (null與undefined/閉包/foreach與map/匿名函式/程式碼組織)

Part 3 (宿主物件與原生物件/函式呼叫方式/call與apply/bind/document.write)

 

  1、解釋事件委託

  事件委託是一種向父級元素增加事件監聽器,而不用向子級元素一個個新增的技術方案。監聽器將在會DOM事件冒泡到父級元素時被觸發。事件委託有以下優勢:

  • 因為我們只需要在父級元素上新增一個監聽器,而不用在每個子元素上新增,所以JS的記憶體佔用會降低很多。
  • 當子元素有移除(或新增)時,我們不用單獨移除(或新增)其事件監聽器。(注:也就是我們常說的動態繫結)

  參考文件:

  https://davidwalsh.name/event-delegate

  https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation

 

  2、解釋JavaScript中的this工作原理

   關於this其實沒有一個統一的解釋,它算是JavaScript中最讓人困惑的一個概念了。一種通俗的解釋就是,this的取值依賴於函式被誰呼叫。我在網上看過很多關於this的解釋,其中Arnav Aggrawal 的解釋應該是最為清楚的,以下是他的觀點:

  • 如果是使用new關鍵字來呼叫函式,那麼函式內部的this就是一個全新的物件。
  • 如果使用apply、call或者bind來呼叫一個函式,函式內部的this就將指向傳入的第一個引數。(注:使用這幾個方法可以改變this的指向)
  • 如果函式被作為一個方法進行呼叫,比如:obj.method() --- 那麼this就該函式的呼叫者。(注:樣例中的obj)
  • 如果函式被獨立呼叫,也就是沒有被上述的幾種情況呼叫。(比如:method())這種情況,this將指向於一個全域性物件,在瀏覽器中是window物件。(nodejs環境中是global)如果是使用嚴格模式的話,那麼全部物件將會是undefined。
  • 如果使用了上述多條規則的話,那麼排序靠前的將優先控制this的指向。
  • 在ES2015中的箭頭函式,將會忽略上述的所有規則,而將取決與函式建立時的作用域。(箭頭函式的作用域在建立時就已經確定,不能更改。想想真是問題終結者...)

關於this更深一層的介紹,請參考:article on Medium

  引用文件:

  https://codeburst.io/the-simple-rules-to-this-in-javascript-35d97f31bde3

      https://stackoverflow.com/a/3127440/1751946

 

  3、解釋原型鏈的原理

  這是一個在面試中經常被問到的問題。JavaScript中的所有物件一個叫prototype的屬性,這個屬性指向於另一個物件。當需要訪問一個物件上的某個屬性時,如果在自身物件上沒有找到該屬性的話,JS引擎會在物件的prototype上進行查詢,找不到的話會繼續在prototype的prototype物件,依次類推直至在某個prototype上找到該屬性,或者到達原型鏈盡頭時停止。JS的這種行為和傳統的繼承概念很像,但原型鏈的內容遠不止於此,更多內容請檢視:delegation than inheritance

  引用文件:

  https://www.quora.com/What-is-prototypal-inheritance/answer/Kyle-Simpson

  https://davidwalsh.name/javascript-objects

 

  4、如果看待AMD和CommonJS

  這兩個都可以實現模組系統,模組系統在ES2015沒有釋出之前並沒有被原生JavaScript所支援。CommonJS是同步的,AMD(Asynchronous Module Definition)是相對非同步的。CommonJS的設計初衷是應用在服務端,而AMD主要用在客戶瀏覽器端,因為它可以支援非同步載入模組。

  另外在語法層面,我也發現AMD相對比較輕量化,而CommonJS更接近別的開發語言中匯入模組的寫法。大多數時候,AMD還是比較冗餘的,因為它會匯入所有的JavaScript程式碼到你的入口檔案中,這樣反而使我們不能從非同步載入中受益。(比如我只需要lodash/map,但AMD會把整個lodash載入進來) CommonJS的語法更為接近Nodejs模組的編寫方式,在服務端和客戶端開發時,使用方式區別並不大。

  值得慶幸的是ES2015的到來,它可以同時支援同步與非同步模組載入,使得我們有了一個統一的解決方案。但目前ES2015的模組系統還沒有被完全支援,所以我們需要使用一些轉換器將ES6編譯為ES5。

  引用文件:

  https://auth0.com/blog/javascript-module-systems-showdown/

  https://stackoverflow.com/questions/16521471/relation-between-commonjs-amd-and-requirejs

 

  5、function foo(){}(); 為什麼這個表達是不是一個IIFE,要如何修改?

  IIFE的意思是自執行函式表示式。JavaScript引擎將會把上面的程式碼看著是function foo(){}和(),也就是說前面是一個函式宣告,後面的()是嘗試去執行這個函式,但是由於沒有執行的函式名,所以這裡會丟擲異常:Uncaught SyntaxError: Unexpected token )。

  有兩個通過增加括號的方法去修復上面的問題:(function foo(){}()) 以及  (function foo(){}())。這種函式並不會暴露在全域性作用域中,你甚至還可以把函式名也去掉,只保留函式體本身。(即匿名函式:(function (){}()) )

  引用文件:

  http://lucybain.com/blog/2014/immediately-invoked-function-expression/

  

相關文章