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

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

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

(文章中斜體字部分爲譯者添加)java

 

目錄:

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

Part 2 (null與undefined/閉包/foreach與map/匿名函數/代碼組織)git

Part 3 (宿主對象與原生對象/函數調用方式/call與apply/bind/document.write)github

 

  一、解釋事件委託

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

  • 由於咱們只須要在父級元素上添加一個監聽器,而不用在每一個子元素上添加,因此JS的內存佔用會下降不少。
  • 當子元素有移除(或新增)時,咱們不用單獨移除(或添加)其事件監聽器。(注:也就是咱們常說的動態綁定)

  參考文檔:express

  https://davidwalsh.name/event-delegate瀏覽器

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

 

  二、解釋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

 

  三、解釋原型鏈的原理

  這是一個在面試中常常被問到的問題。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

 

  四、若是看待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

 

  五、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/

相關文章
相關標籤/搜索