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); 上面的代碼將輸出以下內(nèi)容到控制臺: "122""32""02""112""NaN2"NaN 原因是… 這里的根本問題是,JavaScript(ECMAScript)是一種弱類型語言,它可對值進行自動類型轉(zhuǎn)換,以適應(yīng)正在執(zhí)行的操作。讓我們通過上面的例子來說明這是如何做到的。 例1:1 + "2" + "2" 輸出:"122" 說明: 1 + "2" 是執(zhí)行的第一個操作。由于其中一個運算對象("2")是字符串,JavaScript會假設(shè)它需要執(zhí)行字符串連接,因此,會將 1 的類型轉(zhuǎn)換為 "1", 1 + "2"結(jié)果就是 "12"。然后, "12" + "2" 就是 "122"。 例2: 1 + +"2" + "2" 輸出: "32" 說明:根據(jù)運算的順序,要執(zhí)行的第一個運算是 +"2"(第一個 "2" 前面的額外 + 被視為一元運算符)。因此,JavaScript將 "2" 的類型轉(zhuǎn)換為數(shù)字,然后應(yīng)用一元 + 號(即,將其視為一個正數(shù))。其結(jié)果是,接下來的運算就是 1 + 2 ,這當然是 3。然后我們需要在一個數(shù)字和一個字符串之間進行運算(即, 3 和 "2"),同樣的,JavaScript會將數(shù)值類型轉(zhuǎn)換為字符串,并執(zhí)行字符串的連接,產(chǎn)生 "32"。 例3: 1 + -"1" + "2" 輸出: "02" 說明:這里的解釋和前一個例子相同,除了此處的一元運算符是 - 而不是 +。先是 "1" 變?yōu)?1,然后當應(yīng)用 - 時又變?yōu)榱?-1 ,然后將其與 1相加,結(jié)果為 0,再將其轉(zhuǎn)換為字符串,連接最后的 "2" 運算對象,得到 "02"。 例4: +"1" + "1" + "2" 輸出: "112" 說明:雖然第一個運算對象 "1"因為前綴的一元 + 運算符類型轉(zhuǎn)換為數(shù)值,但又立即轉(zhuǎn)換回字符串,當連接到第二個運算對象 "1" 的時候,然后又和最后的運算對象"2" 連接,產(chǎn)生了字符串 "112"。 例5: "A" - "B" + "2" 輸出: "NaN2" 說明:由于運算符 - 不能被應(yīng)用于字符串,并且 "A" 和 "B" 都不能轉(zhuǎn)換成數(shù)值,因此,"A" - "B"的結(jié)果是 NaN,然后再和字符串 "2" 連接,得到 "NaN2" 。 例6: "A" - "B" + 2 輸出: NaN 說明:參見前一個例子, "A" - "B" 結(jié)果為 NaN。但是,應(yīng)用任何運算符到NaN與其他任何的數(shù)字運算對象,結(jié)果仍然是 NaN。 2、下面的遞歸代碼在數(shù)組列表偏大的情況下會導(dǎo)致堆棧溢出。在保留遞歸模式的基礎(chǔ)上,你怎么解決這個問題? var list = readHugeList();var nextListItem = function() { var item = list.pop(); if (item) { // process the list item... nextListItem(); } }; 潛在的堆棧溢出可以通過修改nextListItem 函數(shù)避免: var list = readHugeList();var nextListItem = function() { var item = list.pop(); if (item) { // process the list item... setTimeout( nextListItem, 0); } }; 堆棧溢出之所以會被消除,是因為事件循環(huán)操縱了遞歸,而不是調(diào)用堆棧。當 nextListItem 運行時,如果 item不為空,timeout函數(shù)(nextListItem)就會被推到事件隊列,該函數(shù)退出,因此就清空調(diào)用堆棧。當事件隊列運行其timeout事件,且進行到下一個 item 時,定時器被設(shè)置為再次調(diào)用 extListItem。因此,該方法從頭到尾都沒有直接的遞歸調(diào)用,所以無論迭代次數(shù)的多少,調(diào)用堆棧保持清空的狀態(tài)。 3、JavaScript中的“閉包”是什么?請舉一個例子。 閉包是一個可以訪問外部(封閉)函數(shù)作用域鏈中的變量的內(nèi)部函數(shù)。閉包可以訪問三種范圍中的變量:這三個范圍具體為:(1)自己范圍內(nèi)的變量,(2)封閉函數(shù)范圍內(nèi)的變量,以及(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的范圍內(nèi)。因此,上面的代碼將輸出如下: outerArg = 123innerArg = 456outerVar = ainnerVar = bglobalVar = xyz 4、下面的代碼將輸出什么: for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, i * 1000 ); } 解釋你的答案。閉包在這里能起什么作用? 上面的代碼不會按預(yù)期顯示值0,1,2,3,和4,而是會顯示5,5,5,5,和5。 原因是,在循環(huán)中執(zhí)行的每個函數(shù)將整個循環(huán)完成之后被執(zhí)行,因此,將會引用存儲在 i中的最后一個值,那就是5。 閉包可以通過為每次迭代創(chuàng)建一個唯一的范圍,存儲范圍內(nèi)變量的每個唯一的值,來防止這個問題,如下: for (var i = 0; i < 5; i++) { (function(x) { setTimeout(function() { console.log(x); }, x * 1000 ); })(i); } 這就會按預(yù)期輸出0,1,2,3,和4到控制臺。 |
|
來自: 好程序員IT > 《Java培訓(xùn)教程》