前端三十|14. [JS] 深拷貝是什麼?如何實現?

Schaos
Schaos’s Blog
Published in
7 min readSep 30, 2019

--

前天的文章 中,我們討論了 JavaScript 的資料型別,其中最特殊的莫過於物件;在這個萬物皆物件的語言中,如何完美的複製物件,也就成了開發過程中頻繁出現的功能需求;相信蠻多讀者多少也聽過深拷貝、淺拷貝這些名詞,接下來就讓我們一起瞧瞧物件拷貝的實作及背後秘辛。

本系列文已經重新編校彙整編輯成冊,並正式出版囉!

前端三十:從 HTML 到瀏覽器渲染的前端開發者必備心法好評販售中!

喜歡我文章內容的讀者們,歡迎您前往購買支持

資料複製

如果我們想要複製資料,而資料型態是 JavaScript 的基本型別,那麼我們可以直接這樣寫:

let num1 = 123
let num2 = num1
num1 = 456
console.log(num2) // 123

上面的範例中,變數 num2 複製自變數 num1,從複製之後,num1 的值便再也不會和 num2 的數值有所關聯。

那如果複製的資料是物件呢?

let obj1 = {
foo: 'bar'
}
let obj2 = obj1
obj1.foo = 'changed'
console.log(obj2.foo) // changed

這次我們宣告了變數 obj2,複製自 obj1,但當 obj1 內的屬性 foo 改變時,obj2foo 也跟著改變了。為什麼會這樣呢?

Call by value

這是因為 JavaScript 中,物件變數所儲存的值,實際上存的是物件所在的記憶體位置:

圖片來自 Huli — 深入探討 JavaScript 中的參數傳遞:call by value 還是 reference?

當我們將 obj2 賦值為 obj1 時,實際上複製的只有那個記憶體位置,但記憶體位置所儲存的物件,還是同一個,也因此當物件改變時,兩個變數都會被影響。

這樣的情況該如何解決呢?這就是今天我們要來討論的問題了。

物件拷貝

最直觀的方法,就是建立一個新物件,將原本資料全部的屬性都複製進去,這樣就可以了吧?例如我們可以透過 Object.assign,將物件全部的屬性倒過去:

--

--