溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

JavaScript中對象的淺復(fù)制和深復(fù)制

發(fā)布時(shí)間:2020-06-13 07:54:08 來源:網(wǎng)絡(luò) 閱讀:184 作者:java架構(gòu)師1 欄目:編程語言

在JavaScript中,如果要復(fù)制一個變量我們應(yīng)該怎么做呢?下面這種做法是最簡單的一種形式:

//把a(bǔ)復(fù)制給b
let a = 12;
let b = a;

這種復(fù)制方法只能適用于基本類型,如果a是對象怎么辦呢?我們先來看看上面的代碼在內(nèi)存中做了什么事:
JavaScript中對象的淺復(fù)制和深復(fù)制
聲明了變量a = 12,棧內(nèi)存會分配一塊區(qū)域來存儲,如上圖所示。把a(bǔ)賦給b,會在棧中重新開辟一塊區(qū)域來存儲b,并且b的值就是a的值。

假如a是對象,內(nèi)存做了什么事呢?來看下面的例子:

let a = {};
let b = a;

JavaScript中對象的淺復(fù)制和深復(fù)制
如圖所示,對象是存儲在堆內(nèi)存中的,棧中保存的是地址值,使用這種方式復(fù)制對象只不過是復(fù)制了該對象的引用而已,對象實(shí)體還是只有一個。那么對象應(yīng)該怎樣復(fù)制呢?對象的復(fù)制其實(shí)也就是產(chǎn)生一個一模一樣的對象,對象包含屬性和方法,我們創(chuàng)建一個新對象,將舊對象的屬性和方法賦給新對象,這樣不就是復(fù)制了一個對象嗎?順著這個思路,我們來看下面的代碼:

function copy(obj){
  //基本類型和函數(shù)直接返回
  if(!(obj instanceof Object) || typeof obj === 'function') return obj;

  let newObj = {};
  if(obj instanceof Array) newObj = [];

  for(let p in obj){
    newObj[p] = obj[p];
  }
  return newObj;
}

let p = {
  name: 'bob',
  friends: ['jack', 'rose']
}
let p2 = copy(p);
console.log(p2);

定義一個copy函數(shù),接收一個參數(shù),用以實(shí)現(xiàn)對象的復(fù)制,如果參數(shù)是基本類型或函數(shù)就直接返回。函數(shù)體內(nèi)聲明一個新對象newObj,然后遍歷參數(shù)obj,將其每一個屬性都賦給newObj,最后返回newObj。接著使用copy方法生成了p的一個復(fù)制對象p2,結(jié)果如下圖所示:
JavaScript中對象的淺復(fù)制和深復(fù)制
但是這樣有一個問題,我們來看下面的代碼:

p2.friends.push('lily');
console.log(p2.friends);//["jack", "rose", "lily"]
console.log(p.friends);//["jack", "rose", "lily"]

我們給p2的friends添加了一個lily,結(jié)果致使p的friends也被同步修改了。下面的內(nèi)存圖可以幫助讀者理解:
JavaScript中對象的淺復(fù)制和深復(fù)制
從圖中可以看出,雖然p和p2分別指向了兩個對象,但是里面的friends屬性還是指向的同一個數(shù)組,問題在于這段代碼:

for(let p in obj){
  newObj[p] = obj[p];
}

只進(jìn)行了對象第一層的復(fù)制,對于對象里面引用類型的屬性,則進(jìn)行了地址值的復(fù)制,這就是所謂的淺復(fù)制,也就是說p.friends和p2.friends是同一個對象:

console.log(p.friends == p2.friends);//true

那如何進(jìn)行徹底的復(fù)制——深復(fù)制呢?思路也很簡單,在遍歷賦值對象屬性的時(shí)候,遇到屬性是引用類型的,也需要把這個屬性展開賦值一下,于是我們可以用遞歸的思想來實(shí)現(xiàn),如下代碼所示:

//深復(fù)制
function deepCopy(obj){
  //基本類型和函數(shù)直接返回
  if(!(obj instanceof Object) || typeof obj === 'function') return obj;

  let newObj = {};
  if(obj instanceof Array) newObj = [];

  for(let p in obj){
    //繼續(xù)復(fù)制對象里面的對象
    newObj[p] = deepCopy(obj[p]);
  }
  return newObj;
}

let p = {
  name: 'bob',
  friends: ['jack', 'rose']
}
let p2 = deepCopy(p);

p2.friends.push('lily');
console.log(p2.friends);//["jack", "rose", "lily"]
console.log(p.friends);//["jack", "rose"]
console.log(p.friends == p2.friends);//false

聲明了deepCopy函數(shù),用于實(shí)現(xiàn)深復(fù)制,函數(shù)體和淺復(fù)制的函數(shù)體基本相同,只是在遍歷要復(fù)制的對象的時(shí)候添加了一個判斷,如果屬性是基本類型或函數(shù)則進(jìn)行賦值操作,否則遞歸調(diào)用deepCopy,繼續(xù)復(fù)制對象里面的對象。接著演示了deepCopy的使用,發(fā)現(xiàn)修改了p2.friends并不會影響p.friends,它們已經(jīng)是兩個對象了。

對于淺復(fù)制,ES6中提供了Object.assign()方法,如下代碼所示:

let p3 = Object.assign({}, p);
console.log(p3.friends == p.friends);//true

不知讀者是否還記得,在JavaScript繼承(四)——原型式繼承中提到過Object.create()方法,從使用效果上來看,Object.create()創(chuàng)建的新對象有點(diǎn)類似淺復(fù)制,只不過新對象和原對象是一種繼承關(guān)系,而Object.assign()創(chuàng)建的新對象和原對象是彼此獨(dú)立的,如下代碼所示:

let p4 = Object.create(p);
console.log(p4.__proto__ === p);//true
console.log(p3.__proto__ === p);//false

感興趣的可以自己來我的Java架構(gòu)群,可以獲取免費(fèi)的學(xué)習(xí)資料,群號:855801563 對Java技術(shù),架構(gòu)技術(shù)感興趣的同學(xué),歡迎加群,一起學(xué)習(xí),相互討論。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI