JavaScript深拷貝、淺拷貝
JavaScript深拷貝、淺拷貝
淺拷貝:淺拷貝只是複製了記憶體地址,如果原地址中的物件改變了,淺拷貝出來的物件也會相應改變。
深拷貝:開闢了一塊新的記憶體存放地址和地址指向的物件,原地址的任何物件改變了,深拷貝出來的物件不變。
淺拷貝陣列(只拷貝第一級陣列):
1.直接遍歷
var arr = [1,2,3,4]; function copy(arg){ var newArr = []; for(var i = 0; i < arr.length; i++) { newArr.push(arr[i]); } return newArr; } var newArry = copy(arr); console.log(newArry); newArry[0] = 10; console.log(newArry); // [10,2,3,4] console.log(arr)// [1,2,3,4]
2.slice
var arr = [1,2,3,4] var copyArr = arr.slice(); copyArr[0] = 10; console.log(copyArr); // [10,2,3,4] console.log(arr); // [1,2,3,4]
slice(start,end) , slice()
方法返回一個數組中複製出來的元素組成新陣列, start
指起始元素下標,end指終止元素下標
當 slice()
不帶任何引數時,預設返回一個和原陣列一樣的新陣列
3.concat()
var arr = [1,2,3,4] var copyArr = arr.concat(); copyArr[0] = 10; console.log(copyArr); // [10,2,3,4] console.log(arr); // [1,2,3,4]
array.concat(array1,array2,.......,arrayN) , concat()
方法用於連線兩個或多個數組(不會改變原陣列,返回被連線陣列的副本)
然而如果第一級陣列元素是物件或陣列,上面三種方式都失效:
var arr = [ {number:1}, {number:2}, {number:3} ] var copyArr = arr.slice(); copyArr[0].number = 10; console.log(copyArr);// [{number: 100}, { number: 2 },{ number: 3 }] console.log(arr); // [{number: 100}, { number: 2 }, { number: 3 }]
淺拷貝物件(如果物件中的值不為陣列或物件)
1.直接遍歷
var obj = { name: "張三", job: "學生" } function copy (arg) { let newobj = {} for(let item in obj) { newobj[item] = obj; } return newobj; } var copyobj = copy(obj) copyobj.name = "李四" console.log(copyobj) // {name: '李四', job:: '學生'} console.log(obj) // {name: '張三', job:: '學生'}
2.ES6的Object.assign
var obj = { name: '張三', job: '學生' } var copyobj = Object.assign({},obj) copyobj.name = '李四' console.log(copyobj) // {name: '李四', job:: '學生'} console.log(obj)// {name: '張三', job:: '學生'}
Object.assign:用於物件的合併,將源物件 (source)
的所有可列舉屬性,複製到目標物件 (target)
,並返回合併後的 target
用法: Object.assign(target, source1, source2) ; 所以 copyObj = Object.assign({}, obj) ; 這段程式碼將會把 obj
中的一級屬性都拷貝到 {}
中,然後將其返回賦給 copyObj
3.ES6擴充套件運算子
var obj = { name: '張三', job: '學生' } var copyobj = {...obj} copyobj.name = '李四' console.log(copyobj) console.log(obj)
擴充套件運算子 (...)
用於取出引數物件的所有可遍歷屬性,拷貝到當前物件之中
深拷貝
JSON.stringify()
和 JSON.parse()
用 JSON.stringify
把物件轉成字串,再用 JSON.parse
把字串轉成新的物件。
但是這種方法也有不少壞處,譬如它會拋棄物件的 constructor
。也就是深拷貝之後,不管這個物件原來的建構函式是什麼,在深拷貝之後都會變成 Object
。
這種方法能正確處理的物件只有 Number
, String
, Boolean
, Array
, 扁平物件,即那些能夠被 json
直接表示的資料結構。 RegExp
物件是無法通過這種方式深拷貝。
也就是說,只有可以轉成 JSON
格式的物件才可以這樣用,像 function
、 undefined
、 symbol
、迴圈引用的物件沒辦法轉成 JSON
。
var obj1 = { fun: function(){ console.log(123) } }; var obj2 = JSON.parse(JSON.stringify(obj1)); console.log(typeof obj1.fun); // 'function' console.log(typeof obj2.fun); // 'undefined' <-- 沒複製
使用遞迴函式實現一個深拷貝的方法:
function deepClone(obj) { let objClone = Array.isArray(obj) ? [] : {}; if(obj && typeof obj === "object") { for(key in obj) { if(obj.hasOwnProperty(key)) { if(obj[key] && typeof obj[key] === "object") { objClone[key] = deepClone(obj[key]); } else { objClone[key] = obj[key]; } } } } return objClone }