1. 程式人生 > 實用技巧 >你所不知道該如何回答的面試題(一)

你所不知道該如何回答的面試題(一)

JS分為哪兩大型別?都有什麼各自的特點?你該如何判斷正確的型別?

發散思維,舉一反三的回答問題,將很多碎片化的知識點進行串聯。

JS分為基本資料型別和引用資料型別。

基本資料型別:String、Number、Boolean、Null、Undefined、Bigint、Symbol

引用資料型別:Object

其中 Number 型別 是浮點型別,在使用中會遇到某些bug,比如 0.1 + 0.2 != 0.3。這是由於計算機採用二進位制語言,0.1 和 0.2 在由十進位制轉為二進位制時是無限迴圈的小數,計算機的記憶體是有限的,會在某個精度點直接捨棄,導致計算機還沒開始計算,一個很小的舍入錯誤就產生了。

對於 Null 型別,雖然 typeof null 會輸入 object,但是這只是JS存在的一個悠久的bug,最初版本的 JS 使用的是32位系統,為了效能考慮使用低位儲存變數的型別資訊,000 開頭的是物件型別,然而 null 表示為全零,所以將它錯誤的判斷為 object

各自的特點:原始型別儲存的是值,存在於棧中;物件型別儲存的是指標,存在於堆中(因為儲存在棧記憶體的必須是大小固定的資料)。並且針對原始型別和物件型別,複製拷貝也是不一樣的。原始型別的複製是深拷貝,而物件型別的複製,根據複製層級分淺拷貝和深拷貝,這是因為對於物件型別,棧中只存放地址(指標),複製的時候,其實是複製地址,而非具體的內容。

淺拷貝的方式:

  • ES6 的 Object.assign()
  • ES7 的...解構運算子
  • 只拷貝一層

深拷貝的方式:

  • JSON.parse(JSON.stringify(src))

  • 採用遞迴呼叫

  • 藉助第三方庫

    • lodash 的cloneDeep(src)
    • jq 的extend(true, result, src1, src2[ ,src3])

深淺拷貝的實現原理 見 原型模式 筆記

物件一般存放在堆空間中,因為堆空間很大,能存很多大的資料,但缺點是分配記憶體和回收記憶體會佔用一定的時間。所以針對棧和堆的回收是不一樣的。

呼叫棧中的資料回收機制是:當一個函式執行結束之後,JavaScript引擎會通過向下移動ESP來銷燬該函式儲存在棧中的執行上下文。

堆中的資料回收機制是建立在代際假說的基礎之上。

代際假說有兩個特點:

  • 不死的物件會活的更久
  • 大部分物件在記憶體中存在的時間很短,簡單來說就是很多物件一經分配記憶體,很快就變得不可訪問。

因此V8中會把堆分為新生代和老生代兩個區域,新生代中存放生存時間短的物件(1 ~ 8M),老生代中存放生存時間久的物件,採用兩種演算法進行回收。

新生代中使用Scavenge演算法。該演算法是把新生代空間對半劃分為兩個區域,一半是物件區域,一半是空閒區域。然後進行迴圈複製(複製的過程中相當於完成了記憶體整理)和翻轉。並且採取了物件晉升策略,凡是經過兩次垃圾回收依然還存活的物件就會被移動到老生區中。

主垃圾回收器回收老生區中的程式碼,由於老生區中物件佔用空間大並且物件存活時間長。所以採用標記-清除的演算法進行回收。

針對型別的判斷,可以通過 typeof instanceof 來進行判斷。

其中 typeof 的缺點是針對Object、Array和Null型別不能區分,他們都返回 Object

instanceof 雖然能夠區分Array、Object和Function,適合用於判斷自定義的類例項物件,但沒法判斷 Number,Boolean,String 等基本型別。

因此需要視具體情況來使用。

不過Object.prototype.toString.call()可以精準判斷資料的各種型別。但寫法比較繁瑣