一篇文章徹底說清 JS 的深拷貝淺拷貝

這篇文章的受眾

  • 第一類,業務需要,急需知道如何深拷貝JS對象的開發者。

  • 第二類,希望紮實JS基礎,將來好去面試官前秀操作的好學者。

寫給第一類讀者

你只需要一行黑科技代碼就可以實現深拷貝

一篇文章徹底說清 JS 的深拷貝淺拷貝

此時 copyObj.arr !== targetObj.arr 已經實現了深拷貝

彆著急走,利用window.JSON的方法做深拷貝存在2個缺點:

  • 如果你的對象裡有函數,函數無法被拷貝下來

  • 無法拷貝copyObj對象原型鏈上的屬性和方法

當然,你明確知道他們的缺點後,如果他的缺點對你的業務需求沒有影響,就可以放心使用了,一行原生代碼就能搞定。

目前我在開發業務場景中,大多還真可以忽略上面2個缺點。往往需要深拷貝的對象裡沒有函數,也不需要拷貝它原型鏈的屬性。

寫給第二類讀者

下面我會盡可能全面的講解清楚JS裡對象的拷貝,要講清楚拷貝,你需要一點點前置知識

你需要的前置知識:

  • 理解JS裡的引用類型和值類型的區別,知道Obj存儲的只是引用

  • 對原型鏈有基本瞭解

關於對象拷貝的全部:

1.深拷貝、淺拷貝是什麼

2.深拷貝、淺拷貝在業務裡的最常見的應用場景

3.深拷貝和淺拷貝的實現方式

4.總結與建議

1.深拷貝、淺拷貝是什麼

我們討論JS對象深拷貝、淺拷貝的前提

只有對象裡嵌套對象的情況下,才會根據需求討論,我們要深拷貝還是淺拷貝。

比如下面這種對象

一篇文章徹底說清 JS 的深拷貝淺拷貝

因為,如果是類似這樣{name: 'ziwei'},沒有嵌套對象的對象的話,就沒必要區分深淺拷貝了。只有在有嵌套的對象時,深拷貝和淺拷貝才有區別

淺拷貝是什麼樣子的 (我們暫時不管具體如何實現,因為下面會單講)

調用shallowCopy()後,obj2拷貝obj1所有的屬性。但是obj2.arr和obj1.arr是不同的引用,指向同一個內存空間

一篇文章徹底說清 JS 的深拷貝淺拷貝

所以, 2個obj經過拷貝後,雖然他們屬性相同,也的確是不同的對象,但他們內部的obj都是指向同一個內存空間,這種我們叫淺拷貝

深拷貝是什麼樣子的 (我們暫時不管具體如何實現,因為下面會單講)

調用deepCopy()後,obj2拷貝obj1所有的屬性,而且obj2.arr和obj1.arr是指向不同的內存空間,

2個obj2除了拷貝了一樣的屬性,沒有任何其他關聯。

一篇文章徹底說清 JS 的深拷貝淺拷貝

所以, 2個obj經過拷貝後,除了拷貝下來相同的屬性之外,沒有任何其他關聯的2個對象,這種我們叫深拷貝

2.深拷貝在業務裡的最常見的應用場景

舉個栗子,業務需求是 : 一個表格展示商品各種信息,點擊【同意】時,是可以彈出對話框調整商品數量的。

這種業務需求下,我們就會用到對象的深拷貝。因為【商品表格】的屬性和【調整商品表格】的屬性幾乎一樣,我們需要拷貝。

下面的偽代碼和圖片就是展示使用淺拷貝存在的問題

一篇文章徹底說清 JS 的深拷貝淺拷貝

這樣得到的adjustTableArr和tableArr裡,內部對象都是相同的,所以就出現了圖中紅線標註的情況,

當我們修改【調整商品表格】裡的商品數量時,【商品表格】也跟著改變了,這並不是我們想要的

一篇文章徹底說清 JS 的深拷貝淺拷貝

而實際上,我們希望這2個表格裡的數據完全獨立,互不干擾,只有在確認調整之後才刷新商品數量。

這種情況下我們就可以使用前面說的深拷貝的一行黑科技

一篇文章徹底說清 JS 的深拷貝淺拷貝

還記得它的缺陷嗎? 對象裡的函數無法被拷貝,原型鏈裡的屬性無法被拷貝。這裡就對業務沒有影響,可以很方便的深拷貝。

3.深拷貝和淺拷貝的實現方式

其實JQ裡已經有$.extend()函數,實現就是深拷貝和淺拷貝的功能。有興趣的小夥伴也可以看看源碼。

淺拷貝

淺拷貝比較簡單,就是用for in 循環賦值

一篇文章徹底說清 JS 的深拷貝淺拷貝

深拷貝的實現

  • 深拷貝,就是遍歷那個被拷貝的對象

  • 判斷對象裡每一項的數據類型

  • 如果不是對象類型,就直接賦值,如果是對象類型,就再次調用deepCopy,遞歸的去賦值。


一篇文章徹底說清 JS 的深拷貝淺拷貝

以上的無論深、淺拷貝,都用了source.hasOwnProperty(key),意思是判斷這一項是否是其自有屬性,是的話才拷貝,不是就不拷貝。

也就是說__proto__上面的屬性,我不拷貝。這個其實你可以根據業務需求,來決定加上和這個條件

(JQ的$.extend()是會連__proto__上的屬性也拷貝下來的,但是是直接拷貝到對象上,而不是放到之前的__proto__上)

4.總結與建議

雖然大家可能經常用框架提供的api來實現深拷貝。

這篇文章分享的目的,更多還是希望用一篇文章整理清楚深淺拷貝的含義、遞歸實現思路,以及小夥伴們如果使用了JSON.parse()這種黑科技,一定要清楚這樣寫的優缺點。

5.修正

上面的deepCopy方法有漏洞,沒有考慮source一開始就是數組的情況

下面是一個修改後版本

一篇文章徹底說清 JS 的深拷貝淺拷貝

https://segmentfault.com/a/119000001282838


分享到:


相關文章: