Java教程分享JavaScript面試問題及答案(二) 1、.寫一個簡單的函數(shù)(少于80個字符),要求返回一個布爾值指明字符串是否為回文結(jié)構(gòu)。 下面這個函數(shù)在str是回文結(jié)構(gòu)的時候返回true,否則,返回false。 functionisPalindrome(str){ str=str.replace(/\W/g,'').toLowerCase();return(str==str.split('').reverse().join('')); } 例如: console.log(isPalindrome("level"));//logs'true'console.log(isPalindrome("levels"));//logs'false'console.log(isPalindrome("Acar,aman,amaraca"));//logs'true' 2、.寫一個sum方法,在使用下面任一語法調(diào)用時,都可以正常工作。 console.log(sum(2,3));//Outputs5console.log(sum(2)(3));//Outputs5 (至少)有兩種方法可以做到: 方法1 functionsum(x){if(arguments.length==2){returnarguments[0]+arguments[1]; }else{returnfunction(y){returnx+y;}; } } 在JavaScript中,函數(shù)可以提供到arguments對象的訪問,arguments對象提供傳遞到函數(shù)的實際參數(shù)的訪問。這使我們能夠使用length屬性來確定在運行時傳遞給函數(shù)的參數(shù)數(shù)量。 如果傳遞兩個參數(shù),那么只需加在一起,并返回。 否則,我們假設(shè)它被以sum(2)(3)這樣的形式調(diào)用,所以我們返回一個匿名函數(shù),這個匿名函數(shù)合并了傳遞到sum()的參數(shù)和傳遞給匿名函數(shù)的參數(shù)。 方法2 functionsum(x,y){if(y!==undefined){returnx+y; }else{returnfunction(y){returnx+y;}; } } 當(dāng)調(diào)用一個函數(shù)的時候,JavaScript不要求參數(shù)的數(shù)目匹配函數(shù)定義中的參數(shù)數(shù)量。如果傳遞的參數(shù)數(shù)量大于函數(shù)定義中參數(shù)數(shù)量,那么多余參數(shù)將簡單地被忽略。另一方面,如果傳遞的參數(shù)數(shù)量小于函數(shù)定義中的參數(shù)數(shù)量,那么缺少的參數(shù)在函數(shù)中被引用時將會給一個undefined值。所以,在上面的例子中,簡單地檢查第2個參數(shù)是否未定義,就可以相應(yīng)地確定函數(shù)被調(diào)用以及進行的方式。 3、請看下面的代碼片段: for(vari=0;i<5;i++){varbtn=document.createElement('button'); btn.appendChild(document.createTextNode('Button'+i)); btn.addEventListener('click',function(){console.log(i);});document.body.appendChild(btn); } (a)當(dāng)用戶點擊“Button4”的時候會輸出什么到控制臺,為什么?(b)提供一個或多個備用的可按預(yù)期工作的實現(xiàn)方案。 (a)無論用戶點擊什么按鈕,數(shù)字5將總會輸出到控制臺。這是因為,當(dāng)onclick方法被調(diào)用(對于任何按鈕)的時候,for循環(huán)已經(jīng)結(jié)束,變量i已經(jīng)獲得了5的值。(面試者如果能夠談一談有關(guān)如何執(zhí)行上下文,可變對象,激活對象和內(nèi)部“范圍”屬性貢有助于閉包行為,則可以加分)。 (b)要讓代碼工作的關(guān)鍵是,通過傳遞到一個新創(chuàng)建的函數(shù)對象,在每次傳遞通過for循環(huán)時,捕捉到i值。下面是三種可能實現(xiàn)的方法: for(vari=0;i<5;i++){varbtn=document.createElement('button'); btn.appendChild(document.createTextNode('Button'+i)); btn.addEventListener('click',(function(i){returnfunction(){console.log(i);}; })(i));document.body.appendChild(btn); } 或者,你可以封裝全部調(diào)用到在新匿名函數(shù)中的btn.addEventListener: for(vari=0;i<5;i++){varbtn=document.createElement('button'); btn.appendChild(document.createTextNode('Button'+i)); (function(i){ btn.addEventListener('click',function(){console.log(i);}); })(i);document.body.appendChild(btn); } 也可以調(diào)用數(shù)組對象的本地forEach方法來替代for循環(huán): ['a','b','c','d','e'].forEach(function(value,i){varbtn=document.createElement('button'); btn.appendChild(document.createTextNode('Button'+i)); btn.addEventListener('click',function(){console.log(i);});document.body.appendChild(btn); }); 4、下面的代碼將輸出什么到控制臺,為什么? vararr1="john".split('');vararr2=arr1.reverse();vararr3="jones".split(''); arr2.push(arr3);console.log("array1:length="+arr1.length+"last="+arr1.slice(-1));console.log("array2:length="+arr2.length+"last="+arr2.slice(-1)); 輸出結(jié)果是: "array1:length=5last=j,o,n,e,s""array2:length=5last=j,o,n,e,s" arr1和arr2在上述代碼執(zhí)行之后,兩者相同了,原因是: 調(diào)用數(shù)組對象的reverse()方法并不只返回反順序的陣列,它也反轉(zhuǎn)了數(shù)組本身的順序(即,在這種情況下,指的是arr1)。 reverse()方法返回一個到數(shù)組本身的引用(在這種情況下即,arr1)。其結(jié)果為,arr2僅僅是一個到arr1的引用(而不是副本)。因此,當(dāng)對arr2做了任何事情(即當(dāng)我們調(diào)用arr2.push(arr3);)時,arr1也會受到影響,因為arr1和arr2引用的是同一個對象。 這里有幾個側(cè)面點有時候會讓你在回答這個問題時,陰溝里翻船: 傳遞數(shù)組到另一個數(shù)組的push()方法會讓整個數(shù)組作為單個元素映射到數(shù)組的末端。其結(jié)果是,語句arr2.push(arr3);在其整體中添加arr3作為一個單一的元素到arr2的末端(也就是說,它并沒有連接兩個數(shù)組,連接數(shù)組是concat()方法的目的)。 和Python一樣,JavaScript標(biāo)榜數(shù)組方法調(diào)用中的負(fù)數(shù)下標(biāo),例如slice()可作為引用數(shù)組末尾元素的方法:例如,-1下標(biāo)表示數(shù)組中的最后一個元素,等等。 5、下面的代碼將輸出什么到控制臺,為什么? 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,這當(dāng)然是3。然后我們需要在一個數(shù)字和一個字符串之間進行運算(即,3和"2"),同樣的,JavaScript會將數(shù)值類型轉(zhuǎn)換為字符串,并執(zhí)行字符串的連接,產(chǎn)生"32"。 例3:1+-"1"+"2"輸出:"02"說明:這里的解釋和前一個例子相同,除了此處的一元運算符是-而不是+。先是"1"變?yōu)?/span>1,然后當(dāng)應(yīng)用-時又變?yōu)榱?/span>-1,然后將其與1相加,結(jié)果為0,再將其轉(zhuǎn)換為字符串,連接后的"2"運算對象,得到"02"。 例4:+"1"+"1"+"2"輸出:"112"說明:雖然第一個運算對象"1"因為前綴的一元+運算符類型轉(zhuǎn)換為數(shù)值,但又立即轉(zhuǎn)換回字符串,當(dāng)連接到第二個運算對象"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。 6、下面的遞歸代碼在數(shù)組列表偏大的情況下會導(dǎo)致堆棧溢出。在保留遞歸模式的基礎(chǔ)上,你怎么解決這個問題? varlist=readHugeList();varnextListItem=function(){varitem=list.pop();if(item){//processthelistitem... nextListItem(); } }; 潛在的堆棧溢出可以通過修改nextListItem函數(shù)避免: varlist=readHugeList();varnextListItem=function(){varitem=list.pop();if(item){//processthelistitem... setTimeout(nextListItem,0); } }; 堆棧溢出之所以會被消除,是因為事件循環(huán)操縱了遞歸,而不是調(diào)用堆棧。當(dāng)nextListItem運行時,如果item不為空,timeout函數(shù)(nextListItem)就會被推到事件隊列,該函數(shù)退出,因此就清空調(diào)用堆棧。當(dāng)事件隊列運行其timeout事件,且進行到下一個item時,定時器被設(shè)置為再次調(diào)用extListItem。因此,該方法從頭到尾都沒有直接的遞歸調(diào)用,所以無論迭代次數(shù)的多少,調(diào)用堆棧保持清空的狀態(tài)。 7、JavaScript中的“閉包”是什么?請舉一個例子。 閉包是一個可以訪問外部(封閉)函數(shù)作用域鏈中的變量的內(nèi)部函數(shù)。閉包可以訪問三種范圍中的變量:這三個范圍具體為:(1)自己范圍內(nèi)的變量,(2)封閉函數(shù)范圍內(nèi)的變量,以及(3)全局變量。 下面是一個簡單的例子: varglobalVar="xyz"; (functionouterFunc(outerArg){varouterVar='a'; (functioninnerFunc(innerArg){varinnerVar='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 8、下面的代碼將輸出什么: for(vari=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(vari=0;i<5;i++){ (function(x){ setTimeout(function(){console.log(x);},x*1000); })(i); } 這就會按預(yù)期輸出0,1,2,3,和4到控制臺。 9、以下代碼行將輸出什么到控制臺? console.log("0||1="+(0||1));console.log("1||2="+(1||2));console.log("0&&1="+(0&&1));console.log("1&&2="+(1&&2)); 并解釋。 該代碼將輸出: 0||1=11||2=10&&1=01&&2=2 在JavaScript中,||和&&都是邏輯運算符,用于在從左至右計算時,返回第一個可完全確定的“邏輯值”。 或(||)運算符。在形如X||Y的表達(dá)式中,首先計算X并將其解釋執(zhí)行為一個布爾值。如果這個布爾值true,那么返回true(1),不再計算Y,因為“或”的條件已經(jīng)滿足。如果這個布爾值為false,那么我們?nèi)匀徊荒苤?/span>X||Y是真是假,直到我們計算Y,并且也把它解釋執(zhí)行為一個布爾值。 因此,0||1的計算結(jié)果為true(1),同理計算1||2。 與(&&)運算符。在形如X&&Y的表達(dá)式中,首先計算X并將其解釋執(zhí)行為一個布爾值。如果這個布爾值為false,那么返回false(0),不再計算Y,因為“與”的條件已經(jīng)失敗。如果這個布爾值為true,但是,我們?nèi)匀徊恢?/span>X&&Y是真是假,直到我們?nèi)ビ嬎?/span>Y,并且也把它解釋執(zhí)行為一個布爾值。 不過,關(guān)于&&運算符有趣的地方在于,當(dāng)一個表達(dá)式計算為“true”的時候,那么就返回表達(dá)式本身。這很好,雖然它在邏輯表達(dá)式方面計算為“真”,但如果你希望的話也可用于返回該值。這就解釋了為什么,有些令人奇怪的是,1&&2返回2(而不是你以為的可能返回true或1)。 10、執(zhí)行下面的代碼時將輸出什么?請解釋。 console.log(false=='0')console.log(false==='0') 代碼將輸出: truefalse 在JavaScript中,有兩種等式運算符。三個等于運算符===的作用類似傳統(tǒng)的等于運算符:如果兩側(cè)的表達(dá)式有著相同的類型和相同的值,那么計算結(jié)果為true。而雙等于運算符,會只強制比較它們的值。因此,總體上而言,使用===而不是==的做法更好。!==vs!=亦是同理。 21.以下代碼將輸出什么?并解釋你的答案。 vara={}, b={key:'b'},c={key:'c'}; a[b]=123; a[c]=456; console.log(a[b]); 這段代碼將輸出456(而不是123)。 原因為:當(dāng)設(shè)置對象屬性時,JavaScript會暗中字符串化參數(shù)值。在這種情況下,由于b和c都是對象,因此它們都將被轉(zhuǎn)換為"[objectObject]"。結(jié)果就是,a[b]和a[c]均相當(dāng)于a["[objectObject]"],并可以互換使用。因此,設(shè)置或引用a[c]和設(shè)置或引用a[b]完全相同。 |
|