js 陣列深度拷貝詳解

丿灬透?發表於2020-09-30

js 陣列深度拷貝詳解

1.我們已經知道的深拷貝和淺拷貝的區別,在於,深拷貝是拷貝值的同時擁有一個新的儲存地址,而淺拷貝只是拷貝了值,而儲存地址不變;這樣會導致的問題是修改拷貝的值,會同時修改原陣列;

但是你所知道深拷貝真的就是深拷貝嗎?

淺拷貝示例:

var arr = [1,2,3,4]
let newArr = arr  //淺拷貝
newArr.splice(0,1)
console.log(arr,'原陣列');  //[2, 3, 4] "原陣列"
console.log(newArr,'淺拷貝陣列') //[2, 3, 4] "拷貝陣列"

深拷貝示例:

第一種 利用陣列api slice 和 map 返回一新陣列的特性

var arr = [1,2,3,4]
      let newArr = arr.slice(0)  //深拷貝
        newArr.splice(0,1)
        console.log(arr,'原陣列');  //[1, 2, 3, 4] "原陣列"
        console.log(newArr,'深拷貝陣列') //[2, 3, 4] "拷貝陣列"

//map
 let newArr2 = arr.map(item=>{
          return item
        })
        newArr2.splice(0,1)
        console.log(arr,'原陣列');  //[1, 2, 3, 4] "原陣列"
        console.log(newArr2,'深拷貝陣列') //[2, 3, 4] "拷貝陣列"

第二種 建立新陣列儲存

var arr = [1,2,3,4]
let newArr3 = []
          for(var i=0 ;i<arr.length;i++){
            newArr3.push(arr[i])
          }
        newArr3.splice(0,1)
        console.log(arr,'原陣列');  //[1, 2, 3, 4] "原陣列"
        console.log(newArr3,'拷貝陣列') //[ 2, 3, 4] "拷貝陣列"

第三種 利用 JSON.stringify() ,JSON.parse() 之間的轉換

var arr = [1,2,3,4]
let newArr4 =  JSON.parse(JSON.stringify(arr))
         newArr4.splice(0,1)
        console.log(arr,'原陣列');  //[1, 2, 3, 4] "原陣列"
        console.log(newArr4,'拷貝陣列') //[ 2, 3, 4] "拷貝陣列"

但是這種方法有一定缺陷,不能轉換function,還有undefined

var arr2 =  [1,2,3,function(){console.log(11)},undefined,null]
      let newArr4 =  JSON.parse(JSON.stringify(arr2))
         newArr4.splice(0,1)
        console.log(arr2,'原陣列');  //[1, 2, 3, ƒ] "原陣列"
        console.log(newArr4,'拷貝陣列') // [2, 3, null,null,null] "拷貝陣列"

上述方法是能實現深拷貝,但我們需要了解當陣列中有層級,是否能深拷貝呢?
要知道,比方一個陣列 arr =[{a:1,b:2}],這個陣列arr儲存在一個地址中,而其中的arr[0].a儲存的地址卻是裡另一個地址,相當於引用傳遞,這時簡單的深拷貝將不能拷貝這個深層級的拷貝

多層級拷貝錯誤示例:

var arr1 = [{a:1,b:1}]
      let newArr1 = arr1.slice(0)
          newArr1.forEach(item=>{
            delete item.a
          })
        console.log(arr1,'原陣列');  //[{b:1}] "原陣列"
        console.log(newArr1,'拷貝陣列') //[{b:1}] "拷貝陣列"


//第二種
  var arr2 = [[1,2,3],[2,3,4]]
      let newArr2 = []
      for(var i=0 ;i<arr2.length;i++){
            newArr2.push(arr2[i])
          }
        newArr2.forEach(item=>{
            item.splice(0,1)
          })
          console.log(arr2,'原陣列');  //[[2,3],[3,4]] "原陣列"
         console.log(newArr2,'拷貝陣列') //[[2,3],[3,4]] "拷貝陣列"

以上是無法實現多層級拷貝,但是JSON.stringify() ,JSON.parse() 卻可以

var arr1 = [{a:1,b:1}]
let newArr4 =  JSON.parse(JSON.stringify(arr1))
         newArr4.forEach(item=>{
            delete item.a
          })
          console.log(arr1,'原陣列');  //[{a:1,b:1}] "原陣列"
         console.log(newArr4,'拷貝陣列') //[{b:1}] "拷貝陣列"

換一個思路,既然每一層的地址不同,我們何不遍歷每一層的拷貝呢

 var arr1 = [{a:1,b:1}]
 function clone(obj){
      if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; ++i) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }
 }
let newArr5 = clone(arr1) 
newArr5.forEach(item=>{
            delete item.a
          })
          console.log(arr1,'原陣列');  //[{a:1,b:1}] "原陣列"
         console.log(newArr5,'拷貝陣列') //[{b:1}] "拷貝陣列"

當然不同的物件有不同的clone,以下是筆者封裝的方法適用於多種物件

function clone(obj) { 
    // 判斷是否為空未定義
    if (null == obj || "object" != typeof obj) return obj;

    //  Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    //  Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; ++i) {
            copy[i] = clone(obj[i]);
        }
        return copy;
    }

    // Object
    if (obj instanceof Object) {
        var copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("無法複製物件!不支援其型別。");
}

相關文章