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;
}
相关推荐
仟濹5 小时前
【HTML】基础学习【数据分析全栈攻略:爬虫+处理+可视化+报告】
大数据·前端·爬虫·数据挖掘·数据分析·html
小小小小宇6 小时前
前端WebWorker笔记总结
前端
小小小小宇6 小时前
前端监控用户停留时长
前端
小小小小宇6 小时前
前端性能监控笔记
前端
烛阴7 小时前
Date-fns教程:现代JavaScript日期处理从入门到精通
前端·javascript
全栈小57 小时前
【前端】Vue3+elementui+ts,TypeScript Promise<string>转string错误解析,习惯性请出DeepSeek来解答
前端·elementui·typescript·vue3·同步异步
穗余7 小时前
NodeJS全栈开发面试题讲解——P6安全与鉴权
前端·sql·xss
独行soc7 小时前
2025年渗透测试面试题总结-匿名[校招]高级安全工程师(代码审计安全评估)(题目+回答)
linux·安全·web安全·面试·职场和发展·渗透测试
小蜜蜂嗡嗡8 小时前
flutter项目迁移空安全
javascript·安全·flutter
穗余8 小时前
NodeJS全栈开发面试题讲解——P2Express / Nest 后端开发
前端·node.js