JS對象的深拷貝與淺拷貝

一、深拷貝與淺拷貝的概念

在進行JavaScript編程過程中,經常會涉及到對象的拷貝操作。對象的拷貝分為淺拷貝和深拷貝兩種方式。

淺拷貝是指將一個對象複製到另一個對象,產生一個新的對象,新對象和原對象之間的基本類型數據相互獨立,而對於引用類型的數據,依然指向原對象的存儲空間。

深拷貝是指將源對象的值逐個複製到一個新的對象上,並且遞歸地將引用類型的數據也逐個複製到新對象上。這樣,新對象和源對象就互相獨立,互不影響。

二、淺拷貝的實現方式

淺拷貝可以通過Object.assign()或展開運算符(…)來實現。

//使用Object.assign()實現淺拷貝
let obj1 = {a: 1, b: 2};
let obj2 = Object.assign({}, obj1);
console.log(obj2); //{a: 1, b: 2}

//使用展開運算符實現淺拷貝
let obj1 = {a: 1, b: 2};
let obj2 = {...obj1};
console.log(obj2); //{a: 1, b: 2}

值得注意的是,淺拷貝只會複製原對象的第一層數據,如果原對象的某個屬性是引用類型,那麼新對象將使用同一個引用類型數據。

let obj1 = {a: 1, b: [1,2,3]};
let obj2 = {...obj1};
obj2.b.push(4);
console.log(obj1); //{a: 1, b: [1,2,3,4]}
console.log(obj2); //{a: 1, b: [1,2,3,4]}

三、深拷貝的實現方式

實現深拷貝的方式有很多,常用的有遞歸拷貝、JSON.parse()和JSON.stringify()等。

1.遞歸拷貝

遞歸拷貝通過遞歸遍歷原對象,將原對象的每個子元素(屬性)都逐個複製到新對象上,並且對於原對象的每個子元素如果是引用類型數據,就遞歸拷貝這個引用類型數據。

//遞歸拷貝實現函數
function deepClone(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }
  let cloneObj = Array.isArray(obj) ? [] : {};
  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      cloneObj[key] = deepClone(obj[key]);
    }
  }
  return cloneObj;
}

//測試遞歸拷貝函數
let obj1 = {a: 1, b: [1,2,3], c: {d: 4}};
let obj2 = deepClone(obj1);
obj2.b.push(4);
obj2.c.d = 5;
console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}}
console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}}

遞歸拷貝雖然實現簡單,但是由於遞歸遍歷可能會導致棧溢出,因此它的性能和可用性並不好。下面介紹另外兩種方式。

2.JSON.parse()和JSON.stringify()

將對象轉換為JSON字符串,然後再將JSON字符串轉換為對象的方式可以實現深拷貝,JSON.parse()和JSON.stringfy()就是這樣一種方式。

let obj1 = {a: 1, b: [1,2,3], c: {d: 4}};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj2.b.push(4);
obj2.c.d = 5;
console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}}
console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}}

需要注意的是,通過JSON.stringify()和JSON.parse()方式實現深拷貝,存在一些限制:

  • 這種方式只能複製可枚舉屬性,而不能複製不可枚舉屬性和Symbol類型的屬性。
  • 這種方式不能複製對象的方法。
  • 如果原對象的某個屬性值為undefined、function、symbol,那麼新對象中將不包含這些屬性。
  • 如果原對象有循環引用的情況(即某個屬性引用了對象本身),那麼用JSON.stringify()將會拋出異常。

四、拷貝的性能問題

因為深拷貝要遞歸遍歷整個對象,因此深拷貝的效率一般要比淺拷貝慢得多。在代碼效率要求很高的情況下,應該優先選擇使用淺拷貝而不使用深拷貝。深拷貝可以通過緩存已經處理過的對象來提高效率。

//使用Map緩存已經處理過的對象
function deepCloneWithMap(obj, map = new Map()) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }
  if (map.has(obj)) {
    return map.get(obj);
  }
  let cloneObj = Array.isArray(obj) ? [] : {};
  map.set(obj, cloneObj);
  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      cloneObj[key] = deepCloneWithMap(obj[key], map);
    }
  }
  return cloneObj;
}

//測試遞歸拷貝函數
let obj1 = {a: 1, b: [1,2,3], c: {d: 4}};
let obj2 = deepCloneWithMap(obj1);
obj2.b.push(4);
obj2.c.d = 5;
console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}}
console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}}

代碼示例

//淺拷貝示例
let obj1 = {a: 1, b: [1,2,3]};
let obj2 = {...obj1};
obj2.b.push(4);
console.log(obj1); //{a: 1, b: [1,2,3,4]}
console.log(obj2); //{a: 1, b: [1,2,3,4]}

//遞歸拷貝示例
function deepClone(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }
  let cloneObj = Array.isArray(obj) ? [] : {};
  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      cloneObj[key] = deepClone(obj[key]);
    }
  }
  return cloneObj;
}
let obj1 = {a: 1, b: [1,2,3], c: {d: 4}};
let obj2 = deepClone(obj1);
obj2.b.push(4);
obj2.c.d = 5;
console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}}
console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}}

//JSON.parse和JSON.stringify示例
let obj1 = {a: 1, b: [1,2,3], c: {d: 4}};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj2.b.push(4);
obj2.c.d = 5;
console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}}
console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}}

//使用Map緩存已經處理過的對象的遞歸拷貝示例
function deepCloneWithMap(obj, map = new Map()) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }
  if (map.has(obj)) {
    return map.get(obj);
  }
  let cloneObj = Array.isArray(obj) ? [] : {};
  map.set(obj, cloneObj);
  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      cloneObj[key] = deepCloneWithMap(obj[key], map);
    }
  }
  return cloneObj;
}
let obj1 = {a: 1, b: [1,2,3], c: {d: 4}};
let obj2 = deepCloneWithMap(obj1);
obj2.b.push(4);
obj2.c.d = 5;
console.log(obj1); //{a: 1, b: [1,2,3], c: {d: 4}}
console.log(obj2); //{a: 1, b: [1,2,3,4], c: {d: 5}}

原創文章,作者:GLNQZ,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/372284.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
GLNQZ的頭像GLNQZ
上一篇 2025-04-24 06:40
下一篇 2025-04-24 06:40

相關推薦

發表回復

登錄後才能評論