深入 简单 直观 的理解 深拷贝 浅拷贝问题,以及如何实现深拷贝 浅拷贝,前端面试

本文深入解析JavaScript中的深拷贝和浅拷贝概念,通过实例分析两种拷贝方式的差异。重点讨论了基本数据类型和引用数据类型在拷贝过程中的处理,并提供了实现深拷贝的代码示例,包括一般情况和对象嵌套的情况。帮助读者彻底理解这两种拷贝方式,以便在实际开发中正确使用。
摘要由CSDN通过智能技术生成

笔者最近看了js的深拷贝浅拷贝的问题,刚开始比较难以理解,因为不同文章质量参差不齐,有的越看越懵,但是在看了好几篇文章,外加自己梳理后,慢慢搞清楚了,本文将尽量以通俗易懂简洁的方式讲解相关知识,以供大家更直观深入的理解。

学好深拷贝浅拷贝,这一篇就够了!

目录

首先,要想弄清楚深拷贝,浅拷贝问题,我们必须先弄清楚一个问题:

接下来我们步入正题:深拷贝,浅拷贝

浅拷贝深入讲解:

深拷贝深入讲解:

一.一般的深拷贝

二.对象嵌套时的深拷贝


首先,要想弄清楚深拷贝,浅拷贝问题,我们必须先弄清楚一个问题:

js中的数据类型

js中数据类型共分为两大种:

1.基本数据类型:7个 Number,String,Boolean,null,undefined,symbol独一无二的值,bigint表示任意大的整数。(后两个为ES6新增)

2.引用数据类型:object(对象类型)(其中数组(Array)、函数(function)是一种特殊的对象)

关于这两大种数据类型的差别,主要有两方面:

1.存储在内存里的位置不一样。

基本数据类型是直接存储在中的简单数据段,占据空间小、大小固定,属于被频繁使用的数据。栈是存储基本类型值和执行代码的空间。

引用数据类型是存储在内存中,占据空间大、大小不固定。引用数据类型在中存储了指针,该指针指向堆 中该实体的起始地址,当解释器寻找引用值时,会检索其在栈中的地址,取得地址后从堆中获得实体。

引用数据类型存的值(即地址)在栈,但是值(地址)指向的数据在堆区

2.基本数据类型变量,对应的内存区域存储的直接是值。而引用数据类型存储的是存储的是相关值的地址。

例:(写function 是为了表示year,fruit为局部变量)

function test(){
    var year=2022;
    var fruit=[banana,apple,orange]
}

对应的内存图:

因为year为基本数据类型,所以其存储在栈区,而fruie为引用数据类型,其存的值为地址在栈区,其地址指向的值存在堆区。  

综上,我们可以做一个小结:基本数据类型直接存的值和引用数据类型直接存的值(地址)都存在栈中,而引用数据类型存的地址指向的数据存在堆中。

接下来我们步入正题:深拷贝,浅拷贝

深拷贝浅拷贝都是对于引用数据类型来说的,基本数据类型没有深拷贝浅拷贝之分。

何为拷贝?拷贝就是复制粘贴。何为深拷贝,浅拷贝?我们先看一个例子:

var arr1=[0,1,2,3]
var arr2=arr1;

 将数组arr1的值赋给arr2,如果我们执行语句arr2[0]=66;我们就会发现arr1[0]和arr2[0]的值都会变成66。这就是一个很典型的浅拷贝。

var num1=1;
var num2=num1;

若执行语句num2=200;那么num1的值还是1,num2的值为200,互不影响。

所以何为深拷贝浅拷贝?

1.根本的区别:浅拷贝就是简单的把指向别人的值的一个地址给复制过来,拷贝的深度不够 。深拷贝就是实实在在的把别人的值给复制过来。

2.直观的区别:浅拷贝就是双方不是独立的,还会互相影响;深拷贝是不会影响到彼此,是独立的个体。

所以我们说基本数据类型没有深拷贝浅拷贝之分,都是深拷贝,即互不影响。

现在请记住这一句话(非常重要):

如果我们直接用=用A变量对B变量进行赋值(B=A),我们实际上是将A中直接存的东西(值或地址)赋给了B。

浅拷贝深入讲解:

例:

//定义一个对象
var test={
    tname:"oliver",
    tage:18,
    tcolor:['red','blue','yellow']
}

内存图:

解释一下:因为test是对象,所以他是引用数据类型,所以他在栈区存的是指向内容的地址,而他里面的内容(tname,tage,tcolor)则存在堆区。

对于堆区里面的tcolor是数组(特殊的对象),所以也是引用数据类型,所以存的是地址,指向数组中的内容(也存在堆中)

进行一次浅拷贝:

var test2={};
for(let i in test){
    test2[i]=test[i];
}
test2.tcolor[0]="RED";
console.log(test);
console.log(test2);

运行结果: 

我们发现test和test2里的tcolor[0]都发生了改变.

内存图:

解释一下:test2中依次直接复制了test中的值:test[0]的oliver,test[1]的18,test[2]的地址,所以test2[2]指向了和test[2]中同样的地址

深拷贝深入讲解:

一.一般的深拷贝

还用上面的例子:要想实现深拷贝,见以下代码:

var test2={};
for(let i in test){
    if(typeof test[i]=='object'){//若test[i]为对象类型(数组)即引用数据类型,则进行深拷贝
        test2[i]=[];
            for(let j in test[i]){
                test2[i][j]=test[i][j];
            }
    }
    else{
        test2[i]=test[i];
    }
}
test2.tcolor[0]="RED";
console.log(test);
console.log(test2);

运行结果:

我们发现只有test2中的tcolor[1]发生了变化

内存图:

解释一下:对于数组tcolor,tcolor直接存储的是地址,但是tcolor[i]直接存储的是具体的数值而非地址,所以在代码运行的时候test2[tcolor].[0],test2[tcolor].[0],test2[tcolor].[0]依次被test[tcolor].[i]赋值。

二.对象嵌套时的深拷贝

接下来我们加大一下难度:如果对象里是的属性也是对象,那我们该怎么办?

举个例子吧:

//定义一个对象
var test={
    tname:"oliver",
    tage:18,
    tcat:{
        cname:"fatty",
        cage:3,
        cfood:{
            morning:"fish",
            noon:"potato"
              }
         }
}

我们发现test对象里面套了两层对象(tcat,cfood)

针对这种层层嵌套的问题,我们只能递归去解决,代码如下:

function deepCopy(obj){
    let newObj={}
    for(let i in obj){
        if(typeof obj[i]=='object'){ //若obj[i]为object(即引用数据类型),进行递归
            newObj[i]=deepCopy(obj[i])
        }else{
            newObj[i]=obj[i]
        }
    }
    return newObj;
}
var test2=deepCopy(test);
    
    test2.tcat.cname="FATTY";
    console.log(test);
    console.log(test2);

运行结果:

我们将test2中的tcat的cname修改成了FATTY,只有test2中的值发生了改变,test未改变,深拷贝成功!

码字不易,希望大家多多鼓励!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值