
第0章 前言
之前面試時面試官問到 JS 的淺拷貝和深拷貝,雖說之前有了解過,但因為沒有總結的緣故,答得并不好,故想着把内容整理一番,便照做了。
0-1 場景導入
我們先來看一個例子如下一個例子:
let
是不是答對了呢?
沒錯,以上例子就是我們俗稱的
淺拷貝了。那麼,為什麼會出現上述這種
直接将對象指派給一個新變量,修改新對象的值會改變原對象的值的現象呢?這就要從JS的資料類型存儲方式說起了。
0-2 回顧 · 資料類型
衆所周知,JavaScript存在兩種資料類型分别稱為
基本資料類型和
引用資料類型。
- 基本資料類型 :Boolean、Null、Undefined、Number、String、Symbol(ES6新增)
- 鍵和值均存儲于棧記憶體中。
- 引用資料類型 :Object、Array、Function、RegExp、Date等。
- 鍵和值的位址存儲于在棧記憶體,值存儲于堆記憶體。
第1章 淺拷貝
那麼其實就說得通了:其實在表面上變量裡是直接存儲了一個對象,但實際上是存儲了一個對象的位址,而這個位址指向該對象。
是以,
直接将對象指派給一個新變量,實際上是将原變量的位址指派給了新變量,故你操作新變量的過程,實際上還是操作原對象。
例如以下示例:
// 定義一個數組
第2章 深拷貝(重難點)
2-1 完整實作
那麼,如果我們就想要拷貝一個對象過去并形成獨立的空間呢?
這就要用到我們的
深拷貝技術啦。
我們已知直接将對象指派給一個新變量隻是做了淺拷貝(隻拷貝對象位址)。
如何實作?看代碼:
let
是以,由上例子可知,
深拷貝的原理就是定義一個新的對象,周遊源對象的屬性并賦給新對象的屬性。
2-2 通用的簡易方案
雖然我們剛剛已經實作了對象的深拷貝,但是這樣封裝一個函數的做法内容還是顯得過于冗長了。有的時候我們也隻是想深拷貝一個對象,不存在複用性。這個時候采用完整的封裝函數的做法就未免顯得過于愚鈍。
那麼,我們是否還有更加簡潔的做法呢?
答案是肯定的!(這真令人振奮)
看代碼:
let
如上,是不是一行代碼就輕松解決了對象的深拷貝呢?(如果真有這麼美好就好了...)
實際上并不然。
因為,這種做法其實會忽略值為 undefined、function 和 symbol 的内容。是以啊,想要完整實作深拷貝的功能還是老老實實用第一種函數封裝的方式吧~
2-3 隻對數組适用的簡易方案
雖說數組也是對象,但數組相對标準的對象就比較特殊。它們可以兩種簡易的方案完成數組自身的深拷貝。
2-3-1 通過 slice 實作
slice 是 Array 的 API,其本身的作用是從已有的數組中傳回標明的元素。
// 定義一個數組
2-3-2 通過 concat 實作
concat 是 Array 的 API,其本身的作用是完成數組間的的連接配接。
// 定義一個數組
第3章 總結
- 淺拷貝
- 直接将對象指派給新變量即可實作淺拷貝。
- 實際上是拷貝了一個對象的位址。
- 新對象的值發生改變,舊對象的值也因變而變。
- 深拷貝
- 定義一個新的對象,周遊源對象的屬性并賦給新對象的屬性即可實作深拷貝。
- 深拷貝是完全拷貝了原對象的内容并寄存在新的記憶體空間,指向新的記憶體位址。
- 新對象的值發生改變,舊對象不影響(因為實際上這已經是兩個對象,毫無關聯性了)
- 如圖