js 深拷贝

js深拷贝

在JavaScript中,深拷贝(deep copy)是指创建一个对象的副本,使得副本中的所有属性和嵌套对象都是独立于原始对象的新实例。这意味着修改副本不会影响原始对象,反之亦然。由于JavaScript中的对象是引用类型,默认情况下赋值或浅拷贝只会复制引用而不是实际的数据结构,因此我们需要特别处理以实现深拷贝。

以下是几种常见的实现深拷贝的方法:

1. 使用 JSON.parseJSON.stringify

这是最简单且常用的方法之一,适用于大多数普通对象和数组。然而,这种方法有一些局限性,比如它不能正确处理函数、undefinedSymbolMapSet等特殊类型,以及循环引用的对象。

const original = {
    name: 'Alice',
    age: 25,
    hobbies: ['reading', 'coding']
};

const deepCopy = JSON.parse(JSON.stringify(original));

console.log(deepCopy); // 输出: { name: 'Alice', age: 25, hobbies: [ 'reading', 'coding' ] }

注意:此方法不适合包含复杂数据类型的对象。

2. 使用递归函数

编写一个递归函数来遍历对象的所有属性,并根据属性的类型决定如何复制它们。这种方法可以处理更广泛的数据类型,包括函数、MapSet等。

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

    if (Array.isArray(obj)) {
        const arrCopy = [];
        for (let i = 0; i < obj.length; i++) {
            arrCopy[i] = deepClone(obj[i]);
        }
        return arrCopy;
    }

    if (obj instanceof Set) {
        return new Set([...obj]);
    }

    if (obj instanceof Map) {
        return new Map([...obj]);
    }

    const newObj = {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = deepClone(obj[key]);
        }
    }
    return newObj;
}

// 测试用例
const original = {
    name: 'Alice',
    age: 25,
    hobbies: ['reading', 'coding'],
    settings: new Map([['theme', 'dark'], ['language', 'en']]),
    preferences: new Set(['cats', 'dogs']),
    greet: function() { console.log('Hello!'); }
};

const deepCopy = deepClone(original);
console.log(deepCopy);

3. 使用第三方库

对于更复杂的需求,推荐使用成熟的第三方库,如 Lodash 或者 Immutable.js,这些库提供了经过优化和测试的深拷贝功能。

  • Lodash:
const _ = require('lodash');

const original = {
    name: 'Alice',
    age: 25,
    hobbies: ['reading', 'coding']
};

const deepCopy = _.cloneDeep(original);
console.log(deepCopy);
  • Immutable.js:
const { fromJS } = require('immutable');

const original = fromJS({
    name: 'Alice',
    age: 25,
    hobbies: ['reading', 'coding']
});

const deepCopy = original.toJS();
console.log(deepCopy);

4. 使用 ES6 的 structuredClone API

ES6 引入了 structuredClone 方法,它可以用于创建对象的深拷贝。这个API比 JSON.parse(JSON.stringify()) 更加健壮,因为它能够处理更多的数据类型,如 BigIntDateRegExpMapSet 等,但它仍然不能处理函数和循环引用。

const original = {
    name: 'Alice',
    age: 25,
    hobbies: ['reading', 'coding'],
    date: new Date()
};

const deepCopy = structuredClone(original);
console.log(deepCopy);

总结

选择哪种方法取决于你的具体需求:

  • 如果你需要快速简单的解决方案并且不关心特殊数据类型,可以使用 JSON.parse(JSON.stringify())
  • 如果你需要更广泛的兼容性和控制,编写自己的递归函数可能是最好的选择。
  • 对于生产环境或者需要处理复杂对象的情况,考虑使用像 Lodash 这样的成熟库。
  • 如果你使用的浏览器或Node.js版本支持,structuredClone 是一个现代且强大的选项。

每种方法都有其适用场景,请根据实际情况选择最合适的方式。

image.png

留下你的脚步
推荐阅读