javascript對象和數組的深拷貝與淺拷貝

深拷貝和淺拷貝是針對複雜數據類型來講的,淺拷貝只拷貝一層,而深拷貝是層層拷貝。 這裏區分一下深拷貝和淺拷貝,淺拷貝經過直接賦值的方式(arr2=arr1)來實現arr2數組的建立,實質上只是將arr2指向了數組數據[1,2,3,4,5]在堆內存中的保存地址。也就是說arr一、arr2保存的是同一個堆內存地址。所以改變一個數組,會引發另外一個數組同步改變,由於本質上只有一個數組。 深拷貝則是在堆內存另外開闢一片地址,將數組數據[1,2,3,4,5]存放進去,而後將arr2指向這個新的地址。所以改變一個數組不會影響另外一個數組,兩個數組獨立變化。由於本質是兩個數組。數組

淺拷貝和深拷貝都是對於JS中的引用類型而言的,淺拷貝就只是複製對象的引用(堆和棧的關係,簡單類型Undefined,Null,Boolean,Number和String是存入堆,直接引用,object array 則是存入桟中,只用一個指針來引用值),若是拷貝後的對象發生變化,原對象也會發生變化。只有深拷貝纔是真正地對對象的拷貝。bash

一、淺拷貝

淺拷貝的意思就是隻複製引用(指針),而未複製真正的值。ui

const originArray = [1,2,3,4,5];
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
 
const cloneArray = originArray;
const cloneObj = originObj;
 
console.log(cloneArray); // [1,2,3,4,5]
console.log(originObj); // {a:'a',b:'b',c:Array[3],d:{dd:'dd'}}
 
cloneArray.push(6);
cloneObj.a = {aa:'aa'};
 
console.log(cloneArray); // [1,2,3,4,5,6]
console.log(originArray); // [1,2,3,4,5,6]
 
console.log(cloneObj); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
console.log(originArray); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
複製代碼

上面的代碼是最簡單的利用 = 賦值操做符實現了一個淺拷貝,能夠很清楚的看到,隨着 cloneArray 和 cloneObj 改變,originArray 和 originObj 也隨着發生了變化。spa

二、深拷貝

就是對目標的徹底拷貝,不像淺拷貝那樣只是複製了一層引用,就連值也都複製了。 只要進行了深拷貝,它們老死不相往來,誰也不會影響誰。指針

目前實現深拷貝的方法很少,主要是兩種:code

  • 利用 JSON 對象中的 parse 和 stringify,可是這種簡單粗暴的方法有其侷限性。當值爲undefined、function、symbol 會在轉換過程當中被忽略對象

  • ES6 中 引入了 Object.assgn 方法和 ... 展開運算符也能實現對對象的拷貝。遞歸

  • 利用遞歸,遞歸的思想就很簡單了,就是對每一層的數據都實現一次 建立對象->對象賦值 的操做,簡單粗暴上代碼:內存

function deepClone(source){
  const targetObj = source.constructor === Array ? [] : {}; // 判斷複製的目標是數組仍是對象
  for(let keys in source){ // 遍歷目標
    if(source.hasOwnProperty(keys)){
      if(source[keys] && typeof source[keys] === 'object'){ // 若是值是對象,就遞歸一下
        targetObj[keys] = source[keys].constructor === Array ? [] : {};
        targetObj[keys] = deepClone(source[keys]);
      }else{ // 若是不是,就直接賦值
        targetObj[keys] = source[keys];
      }
    }
  }
  return targetObj;
}
複製代碼

三、數組的深拷貝

  • ES6方法 採用擴展運算符,擴展運算符就是將數組擴展爲逗號隔開的序列。
let arr1 = [1,2,3,4,5];
let arr2 = [...arr1];
複製代碼
  • ES5方法slice 經過數組的slice方法,你們注意slice和splice的區別,slice的英文的意思是「切開」的意思,而splice在英文中是「拼接」的意思。字面意思不一樣,能夠引出用法的不一樣。 兩者區別:slice是從原數組切出一個新數組,原數組不變,返回切出的新數組。 splice經過先切後拼的方式實現原數組的增(先切0個數組元素再拼n個)、刪(先切n個再拼0個)、改(先切n個再拼n個)功能,原數組改變,返回切掉的數組。 而下面所說的concat是真正的拼接,原數組未變,返回拼接後的新數組。
let arr1 = [1,2,3,4,5];
let arr2 = arr1.slice(0)
複製代碼
  • ES5方法concat
let arr1 = [1,2,3,4,5];
let arr2 = arr1.concat()
複製代碼
相關文章
相關標籤/搜索