一行JS代码实现对象的深浅拷贝

#移动云AI智启未来#

字数 877,阅读大约需 5 分钟

在JavaScript编程中,我们常常需要复制对象。直接赋值会出现问题。

let obj1 = { name: "小明", age: 18 };
let obj2 = obj1;
obj2.name = "小红";
console.log(obj1.name); // 输出"小红"

看到问题了吗?修改obj2,obj1也跟着变了。这是由于它们指向同一个内存地址。

浅拷贝的局限性

许多人第一想到的是浅拷贝。

// 扩展运算符
let newObj = { ...oldObj };

// Object.assign
let newObj = Object.assign({}, oldObj);

浅拷贝能解决简单问题,但遇到嵌套对象就不行了。

let user = {
    name: "小明",
    address: {
        city: "北京",
        street: "朝阳路"
    }
};

let userCopy = { ...user };
userCopy.address.city = "上海";

console.log(user.address.city); // 输出"上海"

原始对象的address也被修改了。这不是我们想要的结果。

深拷贝的多种方案

1. JSON方法

let newObj = JSON.parse(JSON.stringify(oldObj));

这个方法很简单,但有明显缺陷:

  • • 函数会被忽略
  • • undefined会被删除
  • • Date对象会变成字符串
  • • 循环引用会报错

2. 手动递归实现

function deepClone(obj) {
    if (obj === null || typeof obj !== "object") {
        return obj;
    }
    
    if (obj instanceof Date) {
        return new Date(obj.getTime());
    }
    
    if (obj instanceof Array) {
        return obj.map(item => deepClone(item));
    }
    
    let newObj = {};
    for (let key in obj) {
        if (obj.hasOwnProperty(key)) {
            newObj[key] = deepClone(obj[key]);
        }
    }
    
    return newObj;
}

这个方法很可靠,但代码量很大。

3. 第三方库

Lodash等库提供了深拷贝功能:

let newObj = _.cloneDeep(oldObj);

需要引入额外库,增加了项目体积。

一行代码的终极方案

目前介绍最简单的方法:

const deepClone = obj => structuredClone(obj);

这就是我们今天的主角:structuredClone

structuredClone详解

基本用法

let original = {
    name: "小明",
    hobbies: ["篮球", "游泳"],
    birthDate: new Date("2000-01-01")
};

let copy = structuredClone(original);

看看以下场景

场景1:状态管理

// 在React中保存状态快照
const saveStateSnapshot = (state) => {
    return structuredClone(state);
};

// 恢复到某个状态
const restoreState = (snapshot) => {
    setState(structuredClone(snapshot));
};

场景2:数据备份

// 备份表单数据
const backupFormData = (formData) => {
    localStorage.setItem('formBackup', 
        JSON.stringify(structuredClone(formData))
    );
};

// 恢复表单数据
const restoreFormData = () => {
    const backup = localStorage.getItem('formBackup');
    if (backup) {
        return structuredClone(JSON.parse(backup));
    }
};

场景3:数据比较

// 深比较两个对象是否相等
const isDeepEqual = (obj1, obj2) => {
    const clone1 = structuredClone(obj1);
    const clone2 = structuredClone(obj2);
    return JSON.stringify(clone1) === JSON.stringify(clone2);
};

处理循环引用

循环引用是深拷贝的难点。

let objA = { name: "A" };
let objB = { name: "B" };
objA.ref = objB;
objB.ref = objA;

// 这会报错
// let copy = JSON.parse(JSON.stringify(objA));

// 这会成功
let copy = structuredClone(objA);

structuredClone能够正确处理循环引用,不会进入死循环。

浏览器兼容性

目前主流浏览器都支持structuredClone:

  • • Chrome 98+ ✅
  • • Firefox 94+ ✅
  • • Safari 15.4+ ✅
  • • Edge 98+ ✅

对于不支持的环境,可以使用polyfill:

if (typeof structuredClone === "undefined") {
    window.structuredClone = function(obj) {
        return JSON.parse(JSON.stringify(obj));
    };
}

注意事项

重大提醒

  • • 不能复制函数
  • • 不能复制DOM元素
  • • 在性能敏感的场景要谨慎使用
  • • 确保目标环境支持该API

使用提议

  • • 在数据处理、状态管理等场景优先使用
  • • 对于简单对象,仍可使用扩展运算符
  • • 定期检查浏览器兼容性

使用方法很简单

const original = { /* 你的对象 */ };
const copy = structuredClone(original);

这一行代码解决了JavaScript开发中的一个大难题。目前你可以专注于业务逻辑,不用再为对象拷贝烦恼了。

© 版权声明

相关文章

1 条评论

  • 宸婧
    宸婧 投稿者

    一行代码简洁明了,可大幅减少代码量,提高开发效率

    回复