浅拷贝和深拷贝

surile
2021-04-09 / 0 评论 / 11 阅读 / 正在检测是否收录...
温馨提示:
本文最后更新于2021年04月12日,已超过538天没有更新,若内容或图片失效,请留言反馈。

前言


在JavaScript中有不同的方法来复制对象,如果你对这些不熟悉的话,很容易掉进陷阱里去,那么我们怎样才能够正确的复制一个对象呢?

读完本文,希望你能明白:

  • 什么是深/浅拷贝,他们跟赋值有什么区别?
  • 深/浅拷贝的实现方式有几种?

浅拷贝与深拷贝


  • 什么是浅拷贝?

浅拷贝是创建一个新对象,这个对象有原始对象属性值得一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象

knbda7lc.png

  • 什么是深拷贝?

深拷贝是将一个对象从内存中完整的拷贝出来一份,从堆内存中开辟了一个新的区域来存放新的对象,且修改新对象不会影响原对象

knbdagg0.png

var a1 = {b: {c: }}}

var a2 = shallowClone(a1) // 浅拷贝方法
a2.b.c === a1.b.c // true 新对象和旧对象指向同一内存

var a3 = deepClone(a3) // 深拷贝对象
a3.b.c === a1.b.c // false 新对象和旧对象不共享内存

总而言之,浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但是深拷贝会另外创造一个一模一样的对象,新对象和旧对象不共享内存,修改新对象不会改到原对象。

赋值和深/浅拷贝的区别


这三者的区别如下,不过比较的前提都是针对引用类型

  • 当我们把一个对象赋值给一个变量时,赋的其实是该对象在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变存储空间的内容,因此,两个对象是联动。
  • 浅拷贝:重新再堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后的对象的引用类型因共享同一块内存,会相互影响。
  • 深拷贝:从堆内存中开辟一个新的区域存放新对象,对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。

我们先来看下面的例子,对比赋值与深/浅拷贝得到的对象修改后对原始对象的影响:

// 对象赋值

let a = {
    name: '夜雨的博客',
    arr: [1,[2,3],4]
}

let b1 = a

a.name = "夜雨"
a.arr[1] = [5,6,7]
console.log("a",a) // a { name: '夜雨', arr: [1,[5,6,7],[4]] }
console.log("b1",b) // b1 { name: '夜雨', arr: [1,[5,6,7],[4]] }
// 浅拷贝

let a = {
    name: '夜雨的博客',
    arr: [1,[2,3],4]
}

let b2 = shallowClone(a)

b2.name = "夜雨"
b2.arr[1] = [5,6,7] // 新旧对象还是共享同一块内存
// 这是一个浅拷贝的方法
function shallowClone(source){
    var target = {};
    for(var i in source){
        if(source.hasOwnProperty(i)){
            target[i] = source[i]
        }
    }
    return target;
}
 
console.log("a",a) // a { name: '夜雨的博客', arr: [1,[5,6,7],4] }
console.log("b2",b) // b2 { name: '夜雨', arr: [1,[5,6,7],4] }
// 深拷贝

let a = {
    name: '夜雨的博客',
    arr: [1,[2,3],4]
}

let b3 = deepClone(a)

b3.name = "夜雨"
b3.arr[1] = [5,6,7]
// 这是一个深拷贝的方法
function deepClone(obj){
    if(obj === null) return 
    if(obj instanceof Date) return new Date(obj)
    if(obj instanceof RegExp) return new RegExp(obj)
    if(typeof obj !== "object") return obj
    let cloneObj = new obj.constructor()
    for(let key in obj){
        if(obj.hasOwnProperty(key)){
            cloneObj(key) = deepClone(obj[key])
        }
    }
    return cloneObj
}

console.log("a",a) // a { name: '夜雨的博客', arr: [1,[2,3],4] }
console.log("b3",b3) // b3 { name: '夜雨', arr: [1,[5,6,7],4] }

上面的例子中,a是原始对象,b1是赋值操作得到的对象,b2是浅拷贝得到的对象,b3是深拷贝得到的对象,通过下面的表格,我们可以很清楚的看到他们对原始数据的影响。
knbeik3h.png

1

评论 (0)

取消