最近將持續翻譯JavaScript面試題,希望對各位有所幫助。
(文章中斜體字部分為譯者新增)
目錄:
Part 1(事件委託/this關鍵字/原型鏈/AMD與CommonJS/自執行函式)
1、null,undefined,變數未宣告有什麼區別?如何判斷一個變數的值是上述幾種情況?
變數未宣告 是指給一個變數賦值時,沒有使用var,let,const關鍵字對變數進行宣告。未宣告的變數會被預設定在全域性,而非當前作用域中。在嚴格模式中,試圖給一個未宣告的變數賦值時,JS會丟擲ReferenceError的異常。就像要避免濫用全域性變數一樣,我們同樣要避免操作未宣告的變數。可以在操作變數時加入try/catch來捕獲未宣告的變數異常。
1 function foo() { 2 x = 1; // 在嚴格模式下將會丟擲異常:ReferenceError 3 } 4 5 foo(); 6 console.log(x); // 1
undefined變數則是指一個變數被宣告,但是未被賦值。這種變數的型別也是 undefined。如果將一個沒有返回值的函式執行結果賦值給一個變數的話,這個變數值也會是undefined。如果判斷一個變數值是不是undefined呢?可以使用typeof 判斷返回值是不是恆等於(===)字串 "undefined"。這裡要注意,不能使用雙等號判斷變數是否等於undefined,因為用null去判斷的話也會返回true。(原文意思是 null == undefined 返回的值也是true)
1 var foo; 2 console.log(foo); // undefined 3 console.log(foo === undefined); // true 4 console.log(typeof foo === 'undefined'); // true 5 6 console.log(foo == null); // true. 不能這麼去判斷,結果不準確 7 8 function bar() {} 9 var baz = bar(); 10 console.log(baz); // undefined
null值變數是指一個變數被賦值為null。雖然null的表面意思是空值,但是它和undefined是完全不一樣的,因為它已經被賦值過了。如果判斷null值?最簡單的方法就是用恆等於進行判斷。同樣要注意的是,這裡也不能使用雙等號來判斷,原因和上面一樣,因為undefined判斷也會返回true。
1 var foo = null; 2 console.log(foo === null); // true 3 4 console.log(foo == undefined); // true.不能這麼去判斷,結果不準確
我有一個個人習慣,就是不會讓變數出現未宣告或者未賦值的情況。我會在變數宣告的同時給它賦值為null,哪怕這個變數後續不再使用。
引用文件:
https://stackoverflow.com/questions/15985875/effect-of-declared-and-undeclared-variables
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/undefined
2、什麼是閉包,為什麼/如何去使用閉包?
閉包是函式和宣告該函式的詞法環境的組合。這裡的語法實際上是指語法作用域使用一個變數的位置就是宣告該變數的位置。閉包函式擁有訪問外部函式變數的能力,哪怕外部函式已經執行完畢。(由於閉包會使得函式中的變數都被儲存在記憶體中,記憶體消耗很大,所以不能濫用閉包,否則會造成網頁的效能問題,在IE中可能導致記憶體洩露)
為什麼要使用閉包?
- 私有資料/使用閉包模擬私有函式。其他應用場景可以參考:module pattern
- Partial applications or currying
引用文件:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36
3、請闡述forEach與map兩個迴圈方法的差別。如果選擇?
為了方便理解這兩個方法,讓我們分別來看這兩個方法的功能。
forEach
- 遍歷陣列中的每個元素
- 為每個元素執行一個回撥函式
- 不會有任何返回值
-
1 const a = [1, 2, 3]; 2 const doubled = a.forEach((num, index) => { 3 // Do something with num and/or index. 4 }); 5 6 // doubled = undefined
map
- 遍歷陣列中的每個元素
- 為每個元素執行改函式,並對應生成一個新元素,最終返回一個新陣列
1 const a = [1, 2, 3]; 2 const doubled = a.map(num => { 3 return num * 2; 4 }); 5 6 // doubled = [2, 4, 6]
所以這兩個方法最主要的區別就在於map會返回一個新陣列。如果你需要接收處理結果,並且不修改原始陣列,那麼map就是最合適的選擇。如果你只是希望單獨迴圈一個陣列的元素,那麼forEach可以勝任。
引用文件:
https://codeburst.io/javascript-map-vs-foreach-f38111822c0f
4、匿名函式有哪些典型的應用場景?
可以用在自執行函式中。自執行函式包含一些程式碼在本地作用域中,這樣變數就被宣告在本地作用域中,而不會汙染全域性變數。
1 (function() { 2 // Some code here. 3 })();
作為一次性使用的回撥函式,不需要在其他地方繼續使用該函式。將回撥函式定義在程式碼的右側,這樣的程式碼看起來整體性與可讀性比較好,同時也不用到處去查詢函式的實現體。
1 setTimeout(function() { 2 console.log('Hello world!'); 3 }, 1000);
作為函式式變成構造器或者Lodash的引數。
1 const arr = [1, 2, 3]; 2 const double = arr.map(function(el) { 3 return el * 2; 4 }); 5 console.log(double); // [2, 4, 6]
引用文件:
https://www.quora.com/What-is-a-typical-usecase-for-anonymous-functions
5、如何進行程式碼組織?(元件模式還是傳統的繼承?)
以前,我用Backbone來建立我的資料模型,它鼓勵使用物件導向的思想,先建立Backbone模型,然後在把方法加上去。
雖然元件模式仍然很棒,但時至今日,我開始使用基於React/Redux的Flux架構,這是一種鼓勵單向資料流動的函數語言程式設計方式。我將使用普通物件來作為model層,然後寫一些純工具類的方法來管理這些物件。和其他的Redux程式一樣,State通過actions和reducers來進行維護,
另外我會盡量避免使用傳統的繼承。如果非要用,我也會遵循以下規則:these rules