JS深拷贝与浅拷贝

在 JavaScript 开发中,数据的复制是一个常见的需求。然而,很多人在使用复制功能时,常常会混淆"深拷贝"和"浅拷贝"的概念,导致代码中出现一些难以察觉的错误。今天,我们就来深入探讨一下深拷贝和浅拷贝的区别。

一、深拷贝与浅拷贝的概念

1. 浅拷贝

浅拷贝是指只复制对象的第一层属性,而不会递归复制对象内部的引用类型属性。换句话说,浅拷贝只复制对象的"表面"属性,如果属性值是一个引用类型(如对象或数组),那么复制的只是引用,而不是引用所指向的实际数据。因此,如果修改了浅拷贝后的对象的引用类型属性,原对象也会受到影响。

2. 深拷贝

深拷贝则是完全复制一个对象,包括对象内部的所有层级。深拷贝会递归地复制对象的每个属性,确保新对象与原对象完全独立,互不影响。这意味着,无论原对象的结构有多复杂,深拷贝后的对象都不会受到原对象的任何影响。

二、浅拷贝的实现方法

1. 直接赋值

直接赋值是最简单的浅拷贝方式,但它并不是真正的拷贝,只是将一个变量的引用赋值给另一个变量。因此,修改任何一个变量都会影响另一个变量。

javascript 复制代码
var stu = {
    name: 'xiejie',
    age: 18
};
var stu2 = stu;
stu2.name = "zhangsan";
console.log(stu); // { name: 'zhangsan', age: 18 }
console.log(stu2); // { name: 'zhangsan', age: 18 }

2. Object.assign 方法

Object.assign 方法可以将一个或多个源对象的可枚举属性复制到目标对象中。如果源对象的属性值是引用类型,Object.assign 只会复制引用,而不是引用所指向的实际数据。

javascript 复制代码
const stu = {
    name: 'xiejie',
    age: 18,
    stuInfo: {
        No: 1,
        score: 100
    }
};
const stu2 = Object.assign({}, stu);
stu2.stuInfo.score = 90;
console.log(stu); // { name: 'xiejie', age: 18, stuInfo: { No: 1, score: 90 } }
console.log(stu2); // { name: 'xiejie', age: 18, stuInfo: { No: 1, score: 90 } }

3. ES6 扩展运算符

ES6 的扩展运算符(...)也可以实现浅拷贝。它的工作原理与 Object.assign 类似,同样只会复制引用类型属性的引用。

javascript 复制代码
const stu = {
    name: 'xiejie',
    age: 18,
    stuInfo: {
        No: 1,
        score: 100
    }
};
const stu2 = {...stu};
stu2.stuInfo.score = 90;
console.log(stu); // { name: 'xiejie', age: 18, stuInfo: { No: 1, score: 90 } }
console.log(stu2); // { name: 'xiejie', age: 18, stuInfo: { No: 1, score: 90 } }

4. 数组的 sliceconcat 方法

对于数组,sliceconcat 方法可以实现浅拷贝。这些方法不会修改原数组,而是返回一个新数组。但如果数组中包含引用类型元素,这些方法只会复制引用。

javascript 复制代码
var arr1 = [1, true, 'Hello', { name: 'xiejie', age: 18 }];
var arr2 = arr1.slice();
arr2[3].age = 19;
console.log(arr1); // [ 1, true, 'Hello', { name: 'xiejie', age: 19 } ]
console.log(arr2); // [ 1, true, 'Hello', { name: 'xiejie', age: 19 } ]

5. jQuery 的 $.extend 方法

在 jQuery 中,$.extend 方法可以实现浅拷贝。如果将第一个参数设置为 false(默认值),则为浅拷贝。

javascript 复制代码
const obj = {
    name: 'wade',
    age: 37,
    friend: {
        name: 'james',
        age: 34
    }
};
const cloneObj = {};
$.extend(cloneObj, obj);
obj.friend.name = 'rose';
console.log(obj); // { name: 'wade', age: 37, friend: { name: 'rose', age: 34 } }
console.log(cloneObj); // { name: 'wade', age: 37, friend: { name: 'rose', age: 34 } }

三、深拷贝的实现方法

1. JSON.parse(JSON.stringify)

这是一种广为人知的深拷贝方法。通过将对象转换为 JSON 字符串,再将字符串解析为对象,可以实现深拷贝。但这种方法有一个缺点:它不能处理函数和正则对象。

javascript 复制代码
const stu = {
    name: 'xiejie',
    age: 18,
    stuInfo: {
        No: 1,
        score: 100
    }
};
const stu2 = JSON.parse(JSON.stringify(stu));
stu2.stuInfo.score = 90;
console.log(stu); // { name: 'xiejie', age: 18, stuInfo: { No: 1, score: 100 } }
console.log(stu2); // { name: 'xiejie', age: 18, stuInfo: { No: 1, score: 90 } }

2. jQuery 的 $.extend 方法

在 jQuery 中,$.extend 方法也可以实现深拷贝。只需将第一个参数设置为 true,即可实现深拷贝。

javascript 复制代码
const obj = {
    name: 'wade',
    age: 37,
    friend: {
        name: 'james',
        age: 34
    }
};
const cloneObj = {};
$.extend(true, cloneObj, obj);
obj.friend.name = 'rose';
console.log(obj); // { name: 'wade', age: 37, friend: { name: 'rose', age: 34 } }
console.log(cloneObj); // { name: 'wade', age: 37, friend: { name: 'james', age: 34 } }

3. 手写递归方法

如果需要更灵活的深拷贝实现,可以手写递归方法。这种方法可以处理各种复杂的数据结构,包括函数和正则对象。

javascript 复制代码
function deepClone(target) {
    if (typeof target !== 'object' || target === null) {
        return target;
    }
    if (Array.isArray(target)) {
        const result = [];
        for (const item of target) {
            result.push(deepClone(item));
        }
        return result;
    }
    if (target.constructor === Date) {
        return new Date(target);
    }
    if (target.constructor === RegExp) {
        return new RegExp(target);
    }
    const result = {};
    for (const key in target) {
        result[key] = deepClone(target[key]);
    }
    return result;
}
相关推荐
辻戋2 小时前
从零实现React Scheduler调度器
前端·react.js·前端框架
徐同保2 小时前
使用yarn@4.6.0装包,项目是react+vite搭建的,项目无法启动,报错:
前端·react.js·前端框架
Qrun3 小时前
Windows11安装nvm管理node多版本
前端·vscode·react.js·ajax·npm·html5
中国lanwp3 小时前
全局 npm config 与多环境配置
前端·npm·node.js
JELEE.4 小时前
Django登录注册完整代码(图片、邮箱验证、加密)
前端·javascript·后端·python·django·bootstrap·jquery
TeleostNaCl6 小时前
解决 Chrome 无法访问网页但无痕模式下可以访问该网页 的问题
前端·网络·chrome·windows·经验分享
前端大卫7 小时前
为什么 React 中的 key 不能用索引?
前端
你的人类朋友7 小时前
【Node】手动归还主线程控制权:解决 Node.js 阻塞的一个思路
前端·后端·node.js
小李小李不讲道理9 小时前
「Ant Design 组件库探索」五:Tabs组件
前端·react.js·ant design
毕设十刻9 小时前
基于Vue的学分预警系统98k51(程序 + 源码 + 数据库 + 调试部署 + 开发环境配置),配套论文文档字数达万字以上,文末可获取,系统界面展示置于文末
前端·数据库·vue.js