前端面試&筆試&錯題指南(二)

Vincent Ko發表於2018-07-24

嗯,小白的進擊之路,繼續來補充了... 又看了一些坑,自己第一次疏忽做錯的,還是用筆記下來,共同進步

恩,面試系列和排坑會在github更新哦,一起準備秋招的小夥伴路過可以star下,一起進步O(∩_∩)O~: 傳送門

JS專項

1. 陣列的神奇變化

請問以下輸出是什麼

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"
複製程式碼

是的,發現兩個輸出一樣,先說這道題的核心,再好好想想吧

  • 陣列不是簡單資料型別(值型別),會儲存在堆中(heap)中,當使用var arr1 = arr2賦值時,只是淺拷貝,拿到了arr2的引用,這樣帶來的問題就是,修改arr1的時候arr2也會收到影響。
  • arr1.push(arr2),這就是為什麼有一個函式叫concatpush會直接把整個陣列push進去,而不會分開搞 搞清楚以上兩點,這個題基本上就解開了。

2.+ - 運算子之惑

以下程式輸出是什麼?

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
複製程式碼

嗯,核心是以下幾點,自己再細細思考

  • - +會隱式轉換為Number型別
  • + 作為運算子出現在String型別前時,會認為需要字串拼接,因此會隱式轉換為String
  • Number包含一個特殊的型別NaN,當對非數字進行Number轉換時,會變為這個。

第一題: 第二條,認為需要字串拼接 1被轉換為1,答案122 第二題: 注意到第二個2前面的+號,是符合第一條的,因此第二個2被轉換為Number型別,答案為32 第三題: 同理,答案02 第五題: 運用(1)(3),顯然是NaN2,第六題同理

3.堆疊溢位之謎

下面的程式碼將會造成棧溢位,請問如何優化,不改變原有邏輯

var list = readHugeList();

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        nextListItem();
    }
};
複製程式碼

答案:

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        setTimeout(nextListItem,0}
};
複製程式碼

首先必須搞清楚,堆疊溢位的原因。

在JS中,不小心的操作或者程式設計習慣,很容易造成堆疊溢位,特別是進行回撥或者迴圈的時候。 引用以下來說明溢位的原因:

原因是每次執行程式碼時,都會分配一定尺寸的棧空間(Windows系統中為1M),每次方法呼叫時都會在棧裡儲存一定資訊(如引數、區域性變數、返回值等等),這些資訊再少也會佔用一定空間,成千上萬個此類空間累積起來,自然就超過執行緒的棧空間了。那麼如何解決此類問題?

這裡介紹兩個思路解決此問題:

  1. 非同步
  2. 閉包

顯然,這裡就是使用的第一種方法,閉包。為什麼使用setTimeout就可以解決問題?我們看下與沒用之前的差別。如果沒有使用setTimeout,那麼函式將在大資料前不斷的回撥,直到最後走到重點,最初的函式才執行結束,釋放記憶體。 但是如果使用了setTimeout,我們知道它是非同步的,即使設定了時間為0,它也允許先執行下面的內容,可以釋放堆疊,從而避免堆疊溢位的問題。 換言之,加了setTimeout,nextListItem函式被壓入事件佇列,函式可以退出,因此每次會清空呼叫堆疊。

閉包 也是一樣的道理,因為這道題要求不修改原有邏輯,第一種是最合適的答案,當然用閉包避免的方法就是返回出來一個函式

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        return nextListItem()
    }
};
複製程式碼

當然,這樣做會改變函式的呼叫方式,我們就需要不斷的呼叫 nextListItem()()() 為了處理這個辦法,可以對其進行進一步的封裝

var nextListItem = function() {
    var item = list.pop();

    if (item) {
        // process the list item...
        return function() {
            return nextListItem()
        }
    }
};

function autoRun(fun) {
    var value = nextListItem();
    while(typeof value === 'function') {
        value = nextListItem()
    }
    return
}
複製程式碼

這樣,就解決堆疊溢位的問題。 這裡閉包的思路來源與堆疊溢位解決方案

4.你真的懂物件(Object)的key嗎?

下面函式的輸出是什麼?

var a={},
    b={key:'b'},
    c={key:'c'};

a[b]=123;
a[c]=456;

console.log(a[b]);
複製程式碼

答案: 輸出是這樣的456,不是123,至少我有有點以外...

原因是什麼呢? 這裡瞭解ES6新的資料型別map的應該就會意識到了,沒錯,物件的key值是隻允許String型別的,這也是為什麼引入了map資料型別了。 好了,那如果把一個物件作為key值,就會呼叫toString方法了。

Object.prototype.toString(obj)會得到什麼呢?沒錯`[object Object]。 那所以

a[b] ==> a["[object Object"] = 123;
a[b] ==> a["[object Object"] = 456;
複製程式碼

答案,顯而易見

5.迴文判斷

請做一個迴文判斷的函式,判斷是否是迴文

答案: 這是一個很簡單、很常規的方法。連結串列是最好的判斷迴文的方法,當然得益於JS陣列的靈活方法,可以更容易實現。

這裡主要考慮了一個健壯性的問題,多了一個正則來檢測:

function check(str) {
    str = str.replace(/\W/g,'').toLowerCase();
    return str === str.split('').reverse().join()
}
複製程式碼

相關文章