JavaScript 对象的深拷贝与浅拷贝

30 阅读3分钟

 在 JavaScript 里,对象的浅拷贝和深拷贝是两个重要概念,它们在处理对象复制时存在明显差异。

浅拷贝

浅拷贝会创建一个新对象,新对象的属性与原对象相同。不过,对于引用类型的属性,浅拷贝仅仅复制其引用,而非对象本身。这就意味着,原对象和新对象会共享这些引用类型的属性。
以下是几种实现浅拷贝的常用方法:

  1. 扩展运算符(Spread Operator)
const original = { a: 1, b: { c: 2 } };
const shallowCopy = { ...original };

console.log(shallowCopy); // { a: 1, b: { c: 2 } }
console.log(shallowCopy.b === original.b); // true(引用相同)

  1. Object.assign()
const original = { a: 1, b: { c: 2 } };
const shallowCopy = Object.assign({}, original);

console.log(shallowCopy); // { a: 1, b: { c: 2 } }
console.log(shallowCopy.b === original.b); // true(引用相同)

  1. 手动复制
function shallowCopy(obj) {
    const copy = {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            copy[key] = obj[key];
        }
    }
    return copy;
}

const original = { a: 1, b: { c: 2 } };
const shallowCopy = shallowCopy(original);

console.log(shallowCopy); // { a: 1, b: { c: 2 } }
console.log(shallowCopy.b === original.b); // true(引用相同)

深拷贝

深拷贝同样会创建一个新对象。但不同的是,深拷贝会递归地复制原对象的所有属性,包括嵌套的对象,从而让新对象和原对象完全独立,不共享任何引用。
下面是实现深拷贝的方法

1.JSON.parse () 和 JSON.stringify ()

const original = { a: 1, b: { c: 2 } };
const deepCopy = JSON.parse(JSON.stringify(original));

console.log(deepCopy); // { a: 1, b: { c: 2 } }
console.log(deepCopy.b === original.b); // false(引用不同)

不过,这种方法存在局限性,它无法处理函数、正则表达式、日期对象等特殊对象。

2. 递归实现深拷贝函数

function deepCopy(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }

    let copy;

    if (Array.isArray(obj)) {
        copy = [];
        for (let i = 0; i < obj.length; i++) {
            copy[i] = deepCopy(obj[i]);
        }
    } else {
        copy = {};
        for (let key in obj) {
            if (obj.hasOwnProperty(key)) {
                copy[key] = deepCopy(obj[key]);
            }
        }
    }

    return copy;
}

const original = { a: 1, b: { c: 2 } };
const deepCopy = deepCopy(original);

console.log(deepCopy); // { a: 1, b: { c: 2 } }
console.log(deepCopy.b === original.b); // false(引用不同)

总结

  • 浅拷贝:仅复制对象的一层属性,适合处理简单对象,能提高性能。
  • 深拷贝:会递归复制所有嵌套对象,适用于需要完全独立副本的场景,但要注意其性能开销和特殊对象处理的局限性。

在实际编程中,你要依据具体需求来选择合适的拷贝方式。如果需要处理复杂对象的深拷贝,也可以考虑使用像 Lodash 的 _.cloneDeep 这样的第三方库。

const _ = require('lodash');

// 定义一个包含嵌套结构的原始对象
const originalObject = {
    name: 'John',
    age: 30,
    address: {
        city: 'New York',
        zip: '10001'
    },
    hobbies: ['reading', 'coding'],
    details: {
        education: {
            degree: 'Master',
            school: 'Columbia University'
        }
    }
};

// 使用lodash的cloneDeep方法进行深拷贝
const clonedObject = _.cloneDeep(originalObject);

// 修改克隆对象的嵌套属性
clonedObject.address.city = 'San Francisco';
clonedObject.hobbies.push('painting');
clonedObject.details.education.school = 'Stanford University';

// 验证原始对象是否保持不变
console.log('原始对象地址:', originalObject.address.city); 
// 输出: New York
console.log('克隆对象地址:', clonedObject.address.city); 
// 输出: San Francisco

console.log('原始对象爱好:', originalObject.hobbies); 
// 输出: ['reading', 'coding']
console.log('克隆对象爱好:', clonedObject.hobbies);
 // 输出: ['reading', 'coding', 'painting']

console.log('原始对象学校:', originalObject.details.education.school); 
// 输出: Columbia University
console.log('克隆对象学校:', clonedObject.details.education.school); 
// 输出: Stanford University

// 验证引用是否不同
console.log('地址对象引用是否相同:', originalObject.address === clonedObject.address); 
// 输出: false
console.log('爱好数组引用是否相同:', originalObject.hobbies === clonedObject.hobbies); 
// 输出: false
console.log('教育对象引用是否相同:', 
originalObject.details.education === clonedObject.details.education); 
// 输出: false