JavaScript常見面試題三:1.下列代碼行1-4如何排序,使之能夠在執行代碼時輸出到控制台? 為什麼?
(function() { console.log(1);
setTimeout(function(){console.log(2)}, 1000);
setTimeout(function(){console.log(3)}, 0);
console.log(4);
})();
序号如下:
1
4
3
2
讓我們先來解釋比較明顯而易見的那部分:
1 和 4之是以放在前面,是因為它們是通過簡單調用 console.log() 而沒有任何延遲輸出的
2 之是以放在 3的後面,是因為 2 是延遲了1000毫秒(即,1秒)之後輸出的,而 3 是延遲了0毫秒之後輸出的。
好的。但是,既然 3 是0毫秒延遲之後輸出的,那麼是否意味着它是立即輸出的呢?如果是的話,那麼它是不是應該在 4 之前輸出,既然 4 是在第二行輸出的?
要回答這個問題,你需要正确了解JavaScript的事件和時間設定。
浏覽器有一個事件循環,會檢查事件隊列和處理未完成的事件。例如,如果時間發生在背景(例如,腳本的 onload 事件)時,浏覽器正忙(例如,處理一個 onclick),那麼事件會添加到隊列中。當onclick處理程式完成後,檢查隊列,然後處理該事件(例如,執行 onload 腳本)。
同樣的, setTimeout() 也會把其引用的函數的執行放到事件隊列中,如果浏覽器正忙的話。
當setTimeout()的第二個參數為0的時候,它的意思是“盡快”執行指定的函數。具體而言,函數的執行會放置在事件隊列的下一個計時器開始。但是請注意,這不是立即執行:函數不會被執行除非下一個計時器開始。這就是為什麼在上述的例子中,調用 console.log(4) 發生在調用 console.log(3) 之前(因為調用 console.log(3) 是通過setTimeout被調用的,是以會稍微延遲)。
2.寫一個簡單的函數(少于80個字元),要求傳回一個布爾值指明字元串是否為回文結構。
下面這個函數在 str 是回文結構的時候傳回true,否則,傳回false。
function isPalindrome(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("A car, a man, a maraca")); // logs 'true'
3.寫一個 sum方法,在使用下面任一文法調用時,都可以正常工作。
console.log(sum(2,3)); // Outputs 5console.log(sum(2)(3)); // Outputs 5
(至少)有兩種方法可以做到:
方法1
function sum(x) { if (arguments.length == 2) { return arguments[0] + arguments[1];
} else { return function(y) { return x + y; };
在JavaScript中,函數可以提供到 arguments 對象的通路,arguments 對象提供傳遞到函數的實際參數的通路。這使我們能夠使用 length 屬性來确定在運作時傳遞給函數的參數數量。
如果傳遞兩個參數,那麼隻需加在一起,并傳回。
否則,我們假設它被以 sum(2)(3)這樣的形式調用,是以我們傳回一個匿名函數,這個匿名函數合并了傳遞到 sum()的參數和傳遞給匿名函數的參數。
方法2
function sum(x, y) { if (y !== undefined) { return x + y;
當調用一個函數的時候,JavaScript不要求參數的數目比對函數定義中的參數數量。如果傳遞的參數數量大于函數定義中參數數量,那麼多餘參數将簡單地被忽略。另一方面,如果傳遞的參數數量小于函數定義中的參數數量,那麼缺少的參數在函數中被引用時将會給一個 undefined值。是以,在上面的例子中,簡單地檢查第2個參數是否未定義,就可以相應地确定函數被調用以及進行的方式。
4.請看下面的代碼片段:
for (var i = 0; i < 5; i++) { var btn = document.createElement('button');
btn.appendChild(document.createTextNode('Button ' + i));
btn.addEventListener('click', function(){ console.log(i); }); document.body.appendChild(btn);
(a)當使用者點選“Button 4”的時候會輸出什麼到控制台,為什麼?(b)提供一個或多個備用的可按預期工作的實作方案。
(a)無論使用者點選什麼按鈕,數字5将總會輸出到控制台。這是因為,當 onclick 方法被調用(對于任何按鈕)的時候, for 循環已經結束,變量 i 已經獲得了5的值。(面試者如果能夠談一談有關如何執行上下文,可變對象,激活對象和内部“範圍”屬性貢有助于閉包行為,則可以加分)。
(b)要讓代碼工作的關鍵是,通過傳遞到一個新建立的函數對象,在每次傳遞通過 for 循環時,捕捉到 i 值。下面是三種可能實作的方法:
btn.addEventListener('click', (function(i) { return function() { console.log(i); };
})(i)); document.body.appendChild(btn);
或者,你可以封裝全部調用到在新匿名函數中的 btn.addEventListener :
(function (i) {
btn.addEventListener('click', function() { console.log(i); });
})(i); document.body.appendChild(btn);
也可以調用數組對象的本地 forEach 方法來替代 for 循環:
['a', 'b', 'c', 'd', 'e'].forEach(function (value, i) { var btn = document.createElement('button');
btn.addEventListener('click', function() { console.log(i); }); document.body.appendChild(btn);
});
5.下面的代碼将輸出什麼到控制台,為什麼?
var arr1 = "john".split('');var arr2 = arr1.reverse();var arr3 = "jones".split('');
arr2.push(arr3);console.log("array 1: length=" + arr1.length + " last=" + arr1.slice(-1));console.log("array 2: length=" + arr2.length + " last=" + arr2.slice(-1));
輸出結果是:
"array 1: length=5 last=j,o,n,e,s""array 2: length=5 last=j,o,n,e,s"
arr1 和 arr2 在上述代碼執行之後,兩者相同了,原因是:
調用數組對象的 reverse() 方法并不隻傳回反順序的陣列,它也反轉了數組本身的順序(即,在這種情況下,指的是 arr1)。
reverse() 方法傳回一個到數組本身的引用(在這種情況下即,arr1)。其結果為,arr2 僅僅是一個到 arr1的引用(而不是副本)。是以,當對 arr2做了任何事情(即當我們調用 arr2.push(arr3);)時,arr1 也會受到影響,因為 arr1 和 arr2 引用的是同一個對象。
這裡有幾個側面點有時候會讓你在回答這個問題時,陰溝裡翻船:
傳遞數組到另一個數組的 push() 方法會讓整個數組作為單個元素映射到數組的末端。其結果是,語句 arr2.push(arr3); 在其整體中添加 arr3 作為一個單一的元素到 arr2 的末端(也就是說,它并沒有連接配接兩個數組,連接配接數組是 concat() 方法的目的)。
和Python一樣,JavaScript标榜數組方法調用中的負數下标,例如 slice() 可作為引用數組末尾元素的方法:例如,-1下标表示數組中的最後一個元素,等等。