手撕算法(3)——拷贝

7 阅读2分钟

浅拷贝

浅拷贝是指创建一个新对象,将原始对象的属性值复制到新对象中。
如果属性是基本数据类型,那么直接复制其值;如果属性是对象或数组等引用类型,复制的是它们的引用而不是值。

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

    const newObj = Array.isArray(obj) ? [] : {};
    for (const key in obj) {  
        if (obj.hasOwnProperty(key)) {
            newObj[key] = obj[key];
        }
    }

    return newObj;
};

key在每次迭代中都只是一个迭代变量,而不是需要重新分配的变量。使用const可以确保key在每次迭代中不会被重新赋值,增加代码的可读性和安全性。

深拷贝

深拷贝是指创建一个新对象,递归地复制原始对象及其所有嵌套的对象,确保原始对象及其副本之间的完全隔离。这样,修改新对象中的属性不会影响原始对象,反之亦然。

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

    const newObj = Array.isArray(obj) ? [] : {};
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = deepCopy(obj[key]);
        }
    }

    return newObj;
};

用例

// 复杂用例:
const complexObject = {
    name: 'John',
    age: 30,
    address: {
        street: '123 Main St',
        city: 'Cityville',
        country: 'Countryland'
    },
    hobbies: ['reading', 'playing guitar'],
    friends: [
        { name: 'Alice', age: 28 },
        { name: 'Bob', age: 32 }
    ]
};

// 测试浅拷贝
const shallowCopyResult2 = shallowCopy(complexObject);
console.log(shallowCopyResult2);
shallowCopyResult2.name = 'Oliver';
shallowCopyResult2.age = 28;
shallowCopyResult2.address.city = 'Villageton';
shallowCopyResult2.hobbies.push('hiking');
shallowCopyResult2.friends[0].name = 'Eva';
console.log(complexObject);  // name和age不变,上面三个操作起作用
console.log(shallowCopyResult2);

// 测试深拷贝
const deepCopyResult2 = deepCopy(complexObject);
console.log(deepCopyResult2);
deepCopyResult2.address.city = 'Huston';
deepCopyResult2.hobbies.push('swimming');
deepCopyResult2.friends[0].name = 'Elen';
console.log(complexObject);  // 上面三个操作不起作用
console.log(deepCopyResult2);

在JavaScript中,对象的比较是基于引用的。 使用===运算符比较两个对象时,它会检查这两个对象是否指向内存中的相同位置,而不是它们的内容是否相同。

// 简单用例
const obj = { a: 1, b: { c: 2 } };

// 测试浅拷贝
const shallowCopyResult1 = shallowCopy(obj);
console.log(shallowCopyResult1); // 应输出 { a: 1, b: { c: 2 } }
console.log(shallowCopyResult1 === obj); // shallowCopyResult1 和 obj 是两个不同的对象,应输出 false
console.log(obj.a === shallowCopyResult1.a); // shallowCopyResult1 中的属性 a 是基本类型,值相等,应输出 true
console.log(obj.b === shallowCopyResult1.b); // shallowCopyResult1 中的属性 b 是引用类型(对象),浅拷贝只复制引用,应输出 true
console.log(obj.b.c === shallowCopyResult1.b.c); // obj.b.c 和 shallowCopyResult1.b.c 都是基本类型,值相等,应输出 true

// 测试深拷贝
const deepCopyResult1 = deepCopy(obj);
console.log(deepCopyResult1); // 应输出 { a: 1, b: { c: 2 } }
console.log(deepCopyResult1 === obj); // deepCopyResult1 和 obj 是两个不同的对象,应输出 false
console.log(obj.a === deepCopyResult1.a); // deepCopyResult1 中的属性 a 是基本类型,值相等,应输出 true
console.log(obj.b === deepCopyResult1.b); // deepCopyResult1 中的属性 b 是引用类型(对象),深拷贝会递归复制,应输出 false
console.log(obj.b.c === deepCopyResult1.b.c); // obj.b.c 和 deepCopyResult1.b.c 都是基本类型,值相等,应输出 true

// 修改原始对象
obj.a = 100;
obj.b.c = 200;

console.log(obj); // 应输出 { a: 100, b: { c: 200 } }
console.log(shallowCopyResult1); // shallowCopyResult1 中的 a 仍然是原值 1,但 b 引用的是原始对象 obj 中的 b,所以 b.c 被修改为 200。应输出 { a: 1, b: { c: 200 } }
console.log(deepCopyResult1); // deepCopyResult1 是深拷贝,不受 obj 修改的影响,保持原样。应输出 { a: 1, b: { c: 2 } }