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;
}
相关推荐
IT_陈寒9 小时前
Vue3性能优化实战:这5个技巧让我的应用加载速度提升了40%
前端·人工智能·后端
LYFlied9 小时前
【每日算法】LeetCode 76. 最小覆盖子串
数据结构·算法·leetcode·面试·职场和发展
小小鸟0089 小时前
Vue响应式原理
前端·javascript·vue.js
努力学算法的蒟蒻9 小时前
day36(12.17)——leetcode面试经典150
算法·leetcode·面试
lee5769 小时前
鄙人的 Vue 3.0 商业级开源甘特图已经发布到 npm
前端·vue.js·npm·开源·甘特图
前端老曹9 小时前
vue3 三级路由无法缓存的终终终终终终极解决方案
前端·javascript·vue.js
零Suger9 小时前
React Router v7数据模式使用指南
javascript·笔记·react.js
1024小神9 小时前
uniapp + vue3 + scss 定义全局样式变量,并使用
前端·uni-app·scss
顾安r9 小时前
12.17 脚本网页 创意导航
java·linux·前端·游戏·html
Q_Q5110082859 小时前
小程序基于Java Web的健身房管理系统设计和开发
java·前端·小程序