在前端開發中,JavaScript是最常用的編程語言之一。在JavaScript開發中,拷貝一個對象是一個非常常見的需求。但是,拷貝的方式不止一種,也有淺拷貝和深拷貝兩種不同的方式。
淺拷貝
淺拷貝是指拷貝一個對象的基本類型的值和引用類型的地址。在淺拷貝中,拷貝后的對象和原始對象會指向同一個地址,如果改變這個地址對應的數據,兩者的值都會改變。下面是一個淺拷貝的例子:
let obj = {a: 1, b: [1, 2, 3]}; let obj2 = Object.assign({}, obj); obj2.b.push(4); console.log(obj); // {a: 1, b: [1, 2, 3, 4]} console.log(obj2); // {a: 1, b: [1, 2, 3, 4]}
可以看到,淺拷貝后改變了obj2.b的值,obj.b的值也跟著改變了。因為淺拷貝只是拷貝了引用類型的地址。
深拷貝
深拷貝是指拷貝一個對象的所有屬性和引用類型的值。在深拷貝中,拷貝后的對象和原始對象不會指向同一個地址,如果改變拷貝后的對象的屬性,原始對象不會受到影響。下面是一個深拷貝的例子:
let obj = {a: 1, b: [1, 2, 3]}; let obj2 = JSON.parse(JSON.stringify(obj)); obj2.b.push(4); console.log(obj); // {a: 1, b: [1, 2, 3]} console.log(obj2); // {a: 1, b: [1, 2, 3, 4]}
可以看到,深拷貝后改變了obj2.b的值,obj.b的值不會受到影響。
深拷貝的注意點
深拷貝雖然能夠拷貝對象的所有屬性和引用類型的值,但需要注意幾個問題:
- 引用類型循環引用的問題
- 拷貝函數的問題
- 拷貝Date類型的問題
下面分別來說明:
引用類型循環引用的問題
假設對象A引用了對象B,而對象B又引用了對象A,這時候做深拷貝就會出現問題。因為深拷貝會無限遞歸,導致js代碼崩潰。下面是一個例子:
let obj = {a: 1}; obj.b = obj; JSON.parse(JSON.stringify(obj)); // Uncaught TypeError: Converting circular structure to JSON
所以,在做深拷貝時需要注意循環引用的問題。
拷貝函數的問題
在做深拷貝時,如果對象包含函數的話,拷貝后的對象里面的函數會丟失。下面是一個例子:
let obj = {a: 1, b: function(){ console.log('hello')}}; let obj2 = JSON.parse(JSON.stringify(obj)); console.log(obj); // {a: 1, b: ?} console.log(obj2); // {a: 1}
可以看到,拷貝后的對象obj2已經沒有函數b了。所以在需要拷貝函數的情況下,深拷貝就不再適用。
拷貝Date類型的問題
在做深拷貝時,如果對象包含日期類型的話,拷貝后的日期類型會變成字符串,需要謹慎使用。下面是一個例子:
let obj = {a: 1, b: new Date()}; let obj2 = JSON.parse(JSON.stringify(obj)); console.log(obj); // {a: 1, b: Tue Sep 21 2021 15:55:15 GMT+0800 (中國標準時間)} console.log(obj2); // {a: 1, b: "2021-09-21T07:55:15.020Z"}
可以看到,拷貝后的日期類型變成了字符串。
總結
淺拷貝和深拷貝是在JavaScript開發中非常常見的需求。淺拷貝只是拷貝了引用類型的地址,所以改變拷貝后的對象的屬性時,原始對象也會跟著改變。深拷貝則是拷貝了所有屬性和引用類型的值,所以改變拷貝后的對象的屬性時,原始對象不會受到影響。在使用深拷貝時需要注意循環引用的問題、拷貝函數的問題和拷貝日期類型的問題。