深拷贝与浅拷贝区别,以及深拷贝常用用法

前言

今天在使用Object.assign()方法时候,虽然知道此方法是浅拷贝,但是在以下代码遇到点不理解,于是顺便理了一下深拷贝与浅拷贝。

let target = { a:1, b: 2 };
let source = { b: 3, c: 4 };
const returnedTarget = Object.assign({},target, source);
returnedTarget.a=5
console.log(target);//{ a: 1, b: 2 }
console.log(returnedTarget);//{ a: 5, b: 3, c: 4 }

傻乎乎以为是浅拷贝,以为target的a也会改为5,结果打脸了。

浅拷贝与深拷贝

简单点来说,就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力。

let a=[1,2,3]
let b=a;
b[0]=0;
console.log(a);//[0, 2, 3]
console.log(b);//[0, 2, 3]

=赋值就是典型的浅拷贝,当改变b的值时候,a的值会跟着改变。
这里就牵扯到基本数据类型与复杂数据类型。

基本数据类型与复杂(引用)数据类型

基本数据类型:number,null,undefined,string,boolean,symbol
复杂数据类型:对象({a:1}),数组:([1,2,3]),函数等。
基本类型-名值存储在栈内存中,例如let a=1;


image.png

当b=a时,栈内存会重新开辟一块内存,


image.png

当修改b时候,a不会改变,这是因为a与b存储在不同的内存中,但这不算是深拷贝,因为深拷贝本身只针对较为复杂的object类型数据。
引用类型--名存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值


image.png

当b=a进行拷贝时,其实复制的是a的引用地址,而并非堆里面的值。


image.png

而当我们a[0]=1时进行数组修改时,由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝了。
image.png

那,要是在堆内存中也开辟一个新的内存专门为b存放值,就像基本类型那样,岂不就达到深拷贝的效果了


image.png

深拷贝方法

1.自己封装一个深拷贝方法

function deepClone(obj){
    let objClone=Array.isArray(obj)?[]:{};
    if(obj&&typeof obj==='object'){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                    if(obj[key]&&typeof obj[key]==='object'){
                        objClone[key]=deepClone(obj[key])
                    }else{
                      objClone[key]=obj[key]
                    }
            }
        }
    }
    return objClone;
}
let a=[1,2,3];
let b=deepClone(a);
b[0]=4;
console.log(b,a)
//Array [4, 2, 3] Array [1, 2, 3]

此方法原理为拷贝对象各个层级的属性,每一层都拥有独立的栈内存。
2.使用JSON对象的parse和stringify

function deepClone(obj){
    let objString=JSON.stringify(obj);
    let objClone=JSON.parse(objString);
    return objClone;
}
let a=[1,2,3];
let b=deepClone(a);
b[0]=4;
console.log(b,a);
//Array [4, 2, 3] Array [1, 2, 3]

总结

写到这里也基本了解了深拷贝与浅拷贝,也知道为什么要使用深拷贝,以及怎么实现深拷贝,关于JS内存空间的文章,若有兴趣欢迎阅读 JS 从内存空间谈到垃圾回收机制 这篇文章。

你可能感兴趣的:(深拷贝与浅拷贝区别,以及深拷贝常用用法)