js中的數組拷貝(淺拷貝,深拷貝)
問題
要拷貝一個內容會變化的數組,使用了=賦值,slice(),concat()方法都不行,修改了原數組后拷貝數組也變了,原因是這個數組內容是object,而object是引用類型,需要使用深拷貝,最后使用var newArr = JSON.parse(JSON.stringify(arr));解決
淺拷貝&深拷貝
- 淺拷貝:如果數組元素是基本類型,就會拷貝一份,互不影響,而如果是對象或者數組,就會只拷貝對象和數組的引用,無論對新舊數組的哪一個進行了修改,兩者都會發生變化。
- 深拷貝:完全的拷貝一個對象,即使嵌套了對象,兩者也相互分離,修改一個對象的屬性,也不會影響另一個。
//原數組 var arr = [{name: 'wens'},{age: '26'}]; //淺拷貝 var newArr1 = arr; //淺拷貝 var newArr2 = arr.slice(); //淺拷貝 var newArr3 = arr.concat(); //深拷貝 var newArr4 = JSON.parse(JSON.stringify(arr)); //改變原數組內對象的值 arr[0].name = 'leon'; arr[1].age = '27'; //輸出最終結果 console.log(arr); console.log(newArr1); console.log(newArr2); console.log(newArr3); console.log(newArr4);
運行結果:
>Array [object { name:"leon"},Object { age:"27"}]
>Array [Object { name:"leon"},Object { age:"27"}]
>Array [Object { name:"leon"},Object { age:"27"}]
>Array [Object { name:"leon"},Object { age:"27"}]
>Array [Object { name:"wens"},Object { age:"26"}]
淺拷貝
1.使用=直接賦值
var newArr = arr;
缺點:由於數組是引用類型,修改了arr或者newArr中的一個會影響全部
2.使用slice()
var newArr = arr.slice();
3.使用concat()
var newArr = arr.concat();
slice()和concat()缺點:當數組內部屬性值為引用對象時,使用slice和concat對對象數組的拷貝,整個拷貝還是淺拷貝,拷貝之后數組各個值的指針還是指向相同的存儲地址。簡單來說就是:
數組中的值如果是引用類型,對其進行增刪改,會影響用slice復制的數組,
但是如果數組中的值是基本類型,就不會影響
JSON.stringify()將值轉換為相應的JSON格式:
- 轉換值如果有toJSON()方法,該方法定義什么值將被序列化。
- 非數組對象的屬性不能保證以特定的順序出現在序列化后的字符串中。
- 布爾值、數字、字符串的包裝對象在序列化過程中會自動轉換成對應的原始值。
- undefined、任意的函數以及symbol值,在序列化過程中會被忽略(出現在非數組對象的屬性值中時)或者被轉換成null(出現在數組中時)。函數、undefined被單獨轉換時,會返回undefined,如JSoN.stringify(function(){})or JSON.stringify(undefined)。
- 對包含循環引用的對象(對象之間相互引用,形成無限循環)執行此方法,會拋出錯誤。
- 所有以symbol 為屬性鍵的屬性都會被完全忽略掉,即便replacer參數中強制指定包含了它們。
- Date日期調用了toJSON()將其轉換為了string字符串(同Date.tolsOString()),因此會被當做字符串處理。
- NaN和Infinity格式的數值及nul都會被當做null。
- 其他類型的對象,包括Map/Set/weakMap/weakSet,僅會序列化可枚舉的屬性。
深拷貝
1.使用JSON.stringify和JSON.parse
不僅可拷貝數組還能拷貝對象(但不能拷貝函數)
var newArr = JSON.parse(JSON.stringify(arr));
缺點:JSON.stringify()有一些局限,比如對於RegExp類型和Function類型則無法完全滿足,而且不支持有循環引用的對象。
2.深拷貝的一個通用方法
實現思路:拷貝的時候判斷屬性值的類型,如果是對象,繼續遞歸調用深拷貝函數
var deepCopy = function(obj) { // 只拷貝對象 if (typeof obj !== 'object') return; // 根據obj的類型判斷是新建一個數組還是一個對象 var newObj = obj instanceof Array ? [] : {}; for (var key in obj) { // 遍歷obj,並且判斷是obj的屬性才拷貝 if (obj.hasOwnProperty(key)) { // 判斷屬性值的類型,如果是對象遞歸調用深拷貝 newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key]; } } return newObj; }