嗯,小白的進擊之路,繼續來補充了... 又看了一些坑,自己第一次疏忽做錯的,還是用筆記下來,共同進步
恩,面試系列和排坑會在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)
,這就是為什麼有一個函式叫concat
,push
會直接把整個陣列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),每次方法呼叫時都會在棧裡儲存一定資訊(如引數、區域性變數、返回值等等),這些資訊再少也會佔用一定空間,成千上萬個此類空間累積起來,自然就超過執行緒的棧空間了。那麼如何解決此類問題?
這裡介紹兩個思路解決此問題:
- 非同步
- 閉包
顯然,這裡就是使用的第一種方法,閉包。為什麼使用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()
}
複製程式碼