js的深拷贝与浅拷贝

前言

深拷贝和浅拷贝是处理对象复制时的两种不同方式。在进入正题前,我们先来简单回顾一下数据类型和常见的数据结构:堆和栈

基本数据类型:Null、Undefined、Number、String、Boolean、Symbol、BigInt

引用数据类型:Function、Array、RegExp、Math、Date、Error、Set、Map等

基本类型值保存在栈中,引用类型值保存在堆中,在栈中保留对象的引用地址,当 JavaScript 访问数据时,通过栈中的引用地址进行访问。故在 JavaScript 中原始类型的赋值会完整复制变量值,而引用类型的赋值则是复制的引用地址

浅拷贝 (Shallow Copy)

浅拷贝只复制对象的第一层属性。如果对象的属性值是基本数据类型(如String、Number、Boolean等),则直接复制值;

如果属性值是引用类型(如Array、Object等),则复制其内存地址,而不是复制实际的值或嵌套的对象。以下都是浅拷贝的实现:

  • Object.assign()
js 复制代码
let origin = { a: '原始字符串', b: { c: 1 } };
let copy = Object.assign({}, origin);
origin.a = '改变后的字符串';
copy.b.c = 2;
console.log(origin); // {a: '改变后的字符串', b: {c: 2}}
console.log(copy); // {a: '原始字符串', b: {c: 2}}
  • 扩展运算符
js 复制代码
let origin = { a: '原始字符串', b: { c: 1 } };
let copy = { ...origin };
origin.a = '改变后的字符串';
copy.b.c = 2;
console.log(origin); // {a: '改变后的字符串', b: {c: 2}}
console.log(copy); // {a: '原始字符串', b: {c: 2}}
  • slice
js 复制代码
let origin = [1, 2, 3, 4];
let copy = origin.slice();
console.log(copy);
  • concat
js 复制代码
let original = [1, 2, 3, 4];
let copy = original.concat();

深拷贝 (Deep Copy)

深拷贝相对于浅拷贝来说,不仅复制了对象本身及其包含的原始类型的值,还复制了所有引用类型的实际值。

这意味着,如果你修改拷贝对象中的一个引用类型的值,原始对象中相应的值不会发生变化,因为它们指向了不同的内存地址。

深拷贝不仅复制对象的第一层属性,还递归复制所有的嵌套对象。这意味着,无论对象有多少层嵌套,深拷贝都会创建所有层次的副本。因此,原始对象和拷贝对象之间不会相互影响。

  • JSON.parse(JSON.stringify(object))
js 复制代码
let original = { a: 1, b: { c: 2 } };
let copy = JSON.parse(JSON.stringify(original));
console.log(copy); // { a: 1, b: { c: 2 } }
JSON.parse(JSON.stringify(NaN))  // null

使用 JSON.stringify() 将对象序列化(转换为JSON字符串),然后使用 JSON.parse() 将字符串解析为新的对象。这种方法不能复制函数和循环引用的对象。

  • 递归拷贝

实现深拷贝的一种方法是递归地复制所有属性。对于每个属性,你可以检查它是否是一个对象,如果是,你再次递归拷贝这个对象;如果不是,直接拷贝其值。

javascript 复制代码
function deepCopy(obj) {
    if (typeof obj !== 'object' || obj === null) {
        return obj;
    }

    let copy;
    if (Array.isArray(obj)) {
        copy = [];
        for (let i = 0; i < obj.length; i++) {
            copy[i] = deepCopy(obj[i]);
        }
    } else {
        copy = {};
        for (let key in obj) {
            if (obj.hasOwnProperty(key)) { // 检查是否是对象自身具有的属性,因为in会包含原型链上的属性,这里只拷贝自身的!
                copy[key] = deepCopy(obj[key]);
            }
        }
    }
    return copy;
}

这种方法递归地复制对象,但是对于特殊情况(如函数、Date、RegExp、循环引用等)需要特殊处理。

js 复制代码
let original = { a: 1, b: { c: 2 } };
let copy = _.cloneDeep(original);
Lodash 的 cloneDeep 方法是一个非常流行的深拷贝实现,它能够处理各种类型的值,并且能管理循环引用。

在选择方法时,需要根据具体需求来决定。如果你需要快速而简单地复制数据,而且不包含复杂对象(如函数、日期、正则表达式等),JSON的方法可能是最快的。

但如果需要一个更稳健和全面的解决方案,那么递归方法或第三方库会更加适用。

总结:

浅拷贝只复制对象的第一层属性,对于属性值是引用类型的,复制的是引用(内存地址)。

深拷贝递归复制所有层次的属性,创建一个完全独立的副本,原对象和拷贝对象之间不会相互影响。

相关推荐
Marry1.08 分钟前
uniapp背景图用本地图片
前端·uni-app
夏河始溢13 分钟前
一七八、Node.js PM2使用介绍
前端·javascript·node.js·pm2
记忆深处的声音14 分钟前
vue2 + Element-ui 二次封装 Table 组件,打造通用业务表格
前端·vue.js·代码规范
陈随易15 分钟前
兔小巢收费引发的论坛调研Node和Deno有感
前端·后端·程序员
熊的猫29 分钟前
webpack 核心模块 — loader & plugins
前端·javascript·chrome·webpack·前端框架·node.js·ecmascript
速盾cdn36 分钟前
速盾:vue的cdn是干嘛的?
服务器·前端·网络
四喜花露水1 小时前
Vue 自定义icon组件封装SVG图标
前端·javascript·vue.js
前端Hardy1 小时前
HTML&CSS: 实现可爱的冰墩墩
前端·javascript·css·html·css3
web Rookie2 小时前
JS类型检测大全:从零基础到高级应用
开发语言·前端·javascript
Au_ust2 小时前
css:基础
前端·css