談深淺拷貝以前咱們先來聊一聊對象javascript
什麼是對象?html
對象用來存儲鍵值對和更復雜的實體,能夠經過花括號 {…}
和其中包含一些可選的屬性來建立。屬性是一個鍵值對,鍵是一個字符串(也叫作屬性名),值能夠是任何類型。java
對象和其餘原始的類型相比有一個很重要的區別,對象都是按引用存儲複製的。git
let user = { name: 'John' }; let admin = user; admin.name = 'Pete'; // 改變 "admin" 的引用 alert(user.name); // 'Pete', changes are seen from the "user" reference
上面的例子展現了只存在一個對象,就像咱們的一個抽屜帶有兩把鑰匙,若是一個鑰匙(admin
)去使用了抽屜,稍後使用另一個鑰匙(user
)打開的時候,就會看到有變化。github
複製一個對象的變量也等同於建立了此對象的另外一個引用。算法
那麼咱們該怎麼複製一個對象呢?建立一份獨立的拷貝,一份複製?code
這也是可行的,可是有一點麻煩,由於JS並無原生的方法支持這麼作。實際上,咱們不多這麼作。複製引用不少時候是好用的。htm
若是咱們真的想這麼作,就須要建立一個新的對象,遍歷現有對象的屬性,在原始值的狀態下複製給新的對象。對象
像這樣:blog
let user = { name: "John", age: 30 }; let clone = {}; // 新的空對象 // 複製全部的屬性值 for (let key in user) { clone[key] = user[key]; } // 如今複製是獨立的複製了 clone.name = "Pete"; // 改變它的值 alert( user.name ); // 原對象屬性值不變
咱們也能夠用Object.assign 來實現。
像這樣:
let user = { name: "John", age: 30 }; let clone = Object.assign({}, user);
它複製了 user
對象全部的屬性給了一個空對象,而後返回拷貝後的對象。事實上,這跟循環賦值同樣,可是更短。
直到如今,咱們是假設全部的 user
屬性都是原始值,可是若是對象屬性指向對象呢?
像這樣:
let user = { name: "John", sizes: { height: 182, width: 50 } }; let clone = Object.assign({}, user); alert( user.sizes === clone.sizes ); // true,同一個對象 // user 和 clone 共享 sizes 對象 user.sizes.width++; // 在這裏改變一個屬性的值 alert(clone.sizes.width); // 51,在這裏查看屬性的值
這個叫淺拷貝,爲了解決上面的的問題,咱們在複製的時候應該檢查 user[key]
的每個值,若是是一個對象,咱們再複製一遍這個對象,這叫作深拷貝。
有一個標準的深拷貝算法,解決上面和一些更復雜的狀況,叫作 Structured cloning algorithm。爲了避免重複造輪子,咱們使用它的一個 JS 實現的庫 lodash, 方法名叫作 _.cloneDeep(obj)。