简述深拷贝和浅拷贝

40 阅读3分钟
  • 浅拷贝
  • 深拷贝

浅拷贝

浅拷贝是指创建一个新对象,新对象与原始对象共享相同的引用类型的属性(如对象、数组),而基本类型的属性会被完全复制。换句话说,浅拷贝只复制对象的引用,而不复制引用指向的内容。
实现浅拷贝的方法有多种,下面列举几种常见的实现方式:

  1. 对象扩展运算符(...)或 Object.assign() 方法:
const originalObject = { name: 'John', age: 30 };
const shallowCopy = { ...originalObject };
// 或
const shallowCopy = Object.assign({}, originalObject);
  1. 数组的 slice() 方法或 concat() 方法:
const originalArray = [1, 2, 3, 4, 5];
const shallowCopy = originalArray.slice();
// 或
const shallowCopy = originalArray.concat();

这些方法会创建一个新的对象或数组,将原始对象的属性复制到新对象中,但如果原始对象的属性是引用类型(如对象、数组),则新对象与原始对象共享相同的引用。这意味着修改新对象的引用类型属性会影响到原始对象。

例如:

const originalObject = { name: 'John', hobbies: ['reading', 'painting'] };
const shallowCopy = { ...originalObject };

shallowCopy.name = 'Jane'; // 修改新对象的基本类型属性不会影响原始对象
shallowCopy.hobbies.push('swimming'); // 修改新对象的引用类型属性会影响原始对象

console.log(originalObject); // { name: 'John', hobbies: ['reading', 'painting', 'swimming'] }
console.log(shallowCopy); // { name: 'Jane', hobbies: ['reading', 'painting', 'swimming'] }

浅拷贝只适用于一层对象的复制,如果对象中的属性仍然是对象或数组,那么这些属性将被共享。

深拷贝

深拷贝是指创建一个新对象,新对象与原始对象完全独立,包括引用类型的属性也会被递归地复制,两个对象之间互不影响。

实现深拷贝的方法有多种,下面列举几种常见的实现方式:

  1. 使用 JSON.parse(JSON.stringify()) 方法(仅适用于 JSON-serializable 对象):
const originalObject = { name: 'John', age: 30 };
const deepCopy = JSON.parse(JSON.stringify(originalObject));

这种方法的优点是简单易用,通过将对象序列化为 JSON 字符串再解析为对象,实现了对象的深拷贝。但需要注意的是,该方法仅适用于 JSON-serializable 对象,即能够被 JSON 格式化的对象,而不能复制包含函数、正则表达式、日期等特殊类型的属性,并且对于循环引用的对象也无法正确处理。

  1. 使用递归函数来手动复制对象的每个属性,处理对象和数组的嵌套:
// 分享几种深拷贝的方法
// 第一种
function deepClone(obj) {
  if (typeof obj !== 'object' || obj === null) {
    return obj;
  }

  let clone = Array.isArray(obj) ? [] : {};
  for (let key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      clone[key] = deepClone(obj[key]);
    }
  }

  return clone;
}

const originalObject = { name: 'John', hobbies: ['reading', 'painting'] };
const deepCopy = deepClone(originalObject);


// 第二种
function deepClone(origin, target) { 
    const localTarget = target || {},
          toStr = Object.prototype.toString,
          arrStr = "[object Array]";
    for(var prop in origin){
        if(Object.prototype.hasOwnProperty.call(origin, prop)){
            if(origin[prop] !== "null" && typeof(prop) == "object"){
                if(toStr.call(origin[prop]) == arrStr){
                    localTarget[prop] = [];
                }
                else{
                    localTarget[prop] = {};
                }   
                //if-else语句可换成
                // target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {}; deepClone(origin[prop],localTarget[prop]);
            }
            else{
                localTarget[prop] = origin[prop];
            }
        }
    }
    return target;
}

这种方法使用递归函数来手动复制对象的每个属性,当遇到对象或数组时,递归地进行深拷贝。这样可以确保每个属性都是独立的副本,两个对象之间互不影响。

使用递归函数实现深拷贝时,要注意处理循环引用的情况,避免无限递归导致栈溢出。

深拷贝是一种更复杂的拷贝方式,相比浅拷贝,它更适用于复制嵌套对象或数组的情况,确保新对象与原始对象完全独立,不受原始对象的变化影响。但需要注意的是,深拷贝可能会导致性能上的损失,因为需要递归地遍历和复制对象的所有属性。因此,在使用深拷贝时要考虑到性能和内存消耗的问题。

文章中若有不恰当的地方或者有什么需要改进的地方,可以在下方评论,感谢