好程序員Java教程分享JavaScript常見面試題四

  好程序員Java教程分享JavaScript常見面試題四1、下面的代碼將輸出什麼到控制檯,爲什麼?

 

console.log(1 + "2" + "2");console.log(1 + +"2" + "2");console.log(1 + -"1" + "2");console.log(+"1" + "1" + "2");console.log( "A" - "B" + "2");console.log( "A" - "B" + 2);

 

上面的代碼將輸出以下內容到控制檯:

 

"122""32""02""112""NaN2"NaN

 

原因是

 

這裏的根本問題是,JavaScript(ECMAScript)是一種弱類型語言,它可對值進行自動類型轉換,以適應正在執行的操作。讓我們通過上面的例子來說明這是如何做到的。

 

11 + "2" + "2" 輸出:"122" 說明: 1 + "2" 是執行的第一個操作。由於其中一個運算對象("2")是字符串,JavaScript會假設它需要執行字符串連接,因此,會將 的類型轉換爲 "1", 1 + "2"結果就是 "12"。然後, "12" + "2" 就是 "122"

 

2: 1 + +"2" + "2" 輸出: "32" 說明:根據運算的順序,要執行的第一個運算是 +"2"(第一個 "2" 前面的額外 被視爲一元運算符)。因此,JavaScript將 "2" 的類型轉換爲數字,然後應用一元 (即,將其視爲一個正數)。其結果是,接下來的運算就是 1 + 2 ,這當然是 3。然後我們需要在一個數字和一個字符串之間進行運算(即, 和 "2"),同樣的,JavaScript會將數值類型轉換爲字符串,並執行字符串的連接,產生 "32"

 

3: 1 + -"1" + "2" 輸出: "02" 說明:這裏的解釋和前一個例子相同,除了此處的一元運算符是 而不是 +。先是 "1" 變爲 1,然後當應用 時又變爲了 -1 ,然後將其與 1相加,結果爲 0,再將其轉換爲字符串,連接最後的 "2" 運算對象,得到 "02"

 

4: +"1" + "1" + "2" 輸出: "112" 說明:雖然第一個運算對象 "1"因爲前綴的一元 運算符類型轉換爲數值,但又立即轉換回字符串,當連接到第二個運算對象 "1" 的時候,然後又和最後的運算對象"2" 連接,產生了字符串 "112"

 

5: "A" - "B" + "2" 輸出: "NaN2" 說明:由於運算符 不能被應用於字符串,並且 "A" 和 "B" 都不能轉換成數值,因此,"A" - "B"的結果是 NaN,然後再和字符串 "2" 連接,得到 "NaN2" 

 

6: "A" - "B" + 2 輸出: NaN 說明:參見前一個例子, "A" - "B" 結果爲 NaN。但是,應用任何運算符到NaN與其他任何的數字運算對象,結果仍然是 NaN

 

2下面的遞歸代碼在數組列表偏大的情況下會導致堆棧溢出。在保留遞歸模式的基礎上,你怎麼解決這個問題?

 

var list = readHugeList();var nextListItem = function() { var item = list.pop(); if (item) { // process the list item...

 

nextListItem();

 

}

 

};

 

潛在的堆棧溢出可以通過修改nextListItem 函數避免:

 

var list = readHugeList();var nextListItem = function() { var item = list.pop(); if (item) { // process the list item...

 

setTimeout( nextListItem, 0);

 

}

 

};

 

堆棧溢出之所以會被消除,是因爲事件循環操縱了遞歸,而不是調用堆棧。當 nextListItem 運行時,如果 item不爲空,timeout函數(nextListItem)就會被推到事件隊列,該函數退出,因此就清空調用堆棧。當事件隊列運行其timeout事件,且進行到下一個 item 時,定時器被設置爲再次調用 extListItem。因此,該方法從頭到尾都沒有直接的遞歸調用,所以無論迭代次數的多少,調用堆棧保持清空的狀態。

 

3JavaScript中的“閉包”是什麼?請舉一個例子。

 

閉包是一個可以訪問外部(封閉)函數作用域鏈中的變量的內部函數。閉包可以訪問三種範圍中的變量:這三個範圍具體爲:(1)自己範圍內的變量,(2)封閉函數範圍內的變量,以及(3)全局變量。

 

下面是一個簡單的例子:

 

var globalVar = "xyz";

 

(function outerFunc(outerArg) { var outerVar = 'a';

 

(function innerFunc(innerArg) { var innerVar = 'b'; console.log( "outerArg = " + outerArg + "\n" + "innerArg = " + innerArg + "\n" + "outerVar = " + outerVar + "\n" + "innerVar = " + innerVar + "\n" + "globalVar = " + globalVar);

 

})(456);

 

})(123);

 

在上面的例子中,來自於 innerFunc, outerFunc和全局命名空間的變量都在 innerFunc的範圍內。因此,上面的代碼將輸出如下:

 

outerArg = 123innerArg = 456outerVar = ainnerVar = bglobalVar = xyz

 

4下面的代碼將輸出什麼:

 

for (var i = 0; i < 5; i++) {

 

setTimeout(function() { console.log(i); }, i * 1000 );

 

}

 

解釋你的答案。閉包在這裏能起什麼作用?

 

上面的代碼不會按預期顯示值0123,和4,而是會顯示5555,和5

 

原因是,在循環中執行的每個函數將整個循環完成之後被執行,因此,將會引用存儲在 i中的最後一個值,那就是5

 

閉包可以通過爲每次迭代創建一個唯一的範圍,存儲範圍內變量的每個唯一的值,來防止這個問題,如下:

 

for (var i = 0; i < 5; i++) {

 

(function(x) {

 

setTimeout(function() { console.log(x); }, x * 1000 );

 

})(i);

 

}

 

這就會按預期輸出0123,和4到控制檯。

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章