阅读 500

深拷贝和浅拷贝的区别

一、赋值(Copy)

赋值是将某一数值或对象赋给某个变量的过程,分为下面 2 部分

基本数据类型:赋值,赋值之后两个变量互不影响 引用数据类型:赋址,两个变量具有相同的引用,指向同一个对象,相互之间有影响 对基本类型进行赋值操作,两个变量互不影响。

    let a = "chenzixu";
    let b = a;
    console.log(b);
    // chenzixu
    
    a = "change";
    console.log(a);
    // change
    console.log(b);
    // chenzixu
复制代码

对引用类型进行赋址操作,两个变量指向同一个对象,改变变量 a 之后会影响变量 b,哪怕改变的只是对象 a 中的基本类型数据。

    let a = {
        name: "chenzixu",
        book: {
            title: "You Don't Know JS",
            price: "45"
        }
    }
    let b = a;
    console.log(b);
    // {
    // 	name: "chenzixu",
    // 	book: {title: "You Don't Know JS", price: "45"}
    // } 

    a.name = "change";
    a.book.price = "55";
    console.log(a);
    // {
    // 	name: "change",
    // 	book: {title: "You Don't Know JS", price: "55"}
    // } 
    
    console.log(b);
    // {
    // 	name: "change",
    // 	book: {title: "You Don't Know JS", price: "55"}
    // } 
复制代码

通常在开发中并不希望改变变量 a 之后会影响到变量 b,这时就需要用到浅拷贝和深拷贝。

二、浅拷贝(Shallow Copy)

1、什么是浅拷贝

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

上图中,SourceObject 是原对象,其中包含基本类型属性 field1 和引用类型属性 refObj。浅拷贝之后基本类型数据 field2 和 filed1 是不同属性,互不影响。但引用类型 refObj 仍然是同一个,改变之后会对另一个对象产生影响。

简单来说可以理解为浅拷贝只解决了第一层的问题,拷贝第一层的基本类型值,以及第一层的引用类型地址。

2、浅拷贝使用场景

Object.assign()

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。

    let a = {
        name: "chenzixu",
        book: {
            title: "You Don't Know JS",
            price: "45"
        }
    }
    let b = Object.assign({}, a);
    console.log(b);
    // {
    // 	name: "muyiy",
    // 	book: {title: "You Don't Know JS", price: "45"}
    // } 
    
    a.name = "change";
    a.book.price = "55";
    console.log(a);
    // {
    // 	name: "change",
    // 	book: {title: "You Don't Know JS", price: "55"}
    // } 
    
    console.log(b);
    // {
    // 	name: "chenzixu",
    // 	book: {title: "You Don't Know JS", price: "55"}
    // } 
复制代码

上面代码改变对象 a 之后,对象 b 的基本属性保持不变。但是当改变对象 a 中的对象 book 时,对象 b 相应的位置也发生了变化。

展开语法 Spread

    let a = {
        name: "chenzixu",
        book: {
            title: "You Don't Know JS",
            price: "45"
        }
    }
    let b = {...a};
    console.log(b);
    // {
    // 	name: "chenzixu",
    // 	book: {title: "You Don't Know JS", price: "45"}
    // } 
    
    a.name = "change";
    a.book.price = "55";
    console.log(a);
    // {
    // 	name: "change",
    // 	book: {title: "You Don't Know JS", price: "55"}
    // } 
    
    console.log(b);
    // {
    // 	name: "muyiy",
    // 	book: {title: "You Don't Know JS", price: "55"}
    // } 
复制代码

Array.prototype.slice()

slice() 方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。

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

a[1] = "99";
a[2][0] = 4;
console.log(a);
// [0, "99", [4, 3]]

console.log(b);
//  ["1", [4, 3]]
复制代码

可以看出,改变 a[1] 之后 b[0] 的值并没有发生变化,但改变 a[2][0] 之后,相应的 b[1][0] 的值也发生变化。说明 slice() 方法是浅拷贝,相应的还有concat等,在工作中面对复杂数组结构要额外注意

三、深拷贝(Deep Copy)

1、什么是深拷贝

2、深拷贝使用场景

JSON.parse(JSON.stringify(object))
let a = {
    name: "chenzixu",
    book: {
        title: "You Don't Know JS",
        price: "45"
    }
}
let b = JSON.parse(JSON.stringify(a));
console.log(b);
// {
// 	name: "chenzixu",
// 	book: {title: "You Don't Know JS", price: "45"}
// } 

a.name = "change";
a.book.price = "55";
console.log(a);
// {
// 	name: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// } 

console.log(b);
// {
// 	name: "chenzixu",
// 	book: {title: "You Don't Know JS", price: "45"}
// } 
复制代码

完全改变变量 a 之后对 b 没有任何影响,这就是深拷贝的魔力。

我们看下对数组深拷贝效果如何。

let a = [0, "1", [2, 3]];
let b = JSON.parse(JSON.stringify( a.slice(1) ));
console.log(b);
// ["1", [2, 3]]

a[1] = "99";
a[2][0] = 4;
console.log(a);
// [0, "99", [4, 3]]

console.log(b);
//  ["1", [2, 3]]
复制代码

在网上查到这些注意事项(虽然现在不懂但是现在记下来,以后会了回来记得解决)

//undefined、symbol 和函数这三种情况,会直接忽略。
let obj = {
name: 'chenzixu',
a: undefined,
b: Symbol('chenzixu'),
c: function() {}
}
console.log(obj);
// {
// 	name: "chenzixu", 
// 	a: undefined, 
//  b: Symbol(chenzixu), 
//  c: ƒ ()
// }

let b = JSON.parse(JSON.stringify(obj));
console.log(b);
// {name: "chenzixu"}
复制代码

循环引用情况下,会报错。

    let obj = {
        a: 1,
        b: {
            c: 2,
       		d: 3
        }
    }
    obj.a = obj.b;
    obj.b.c = obj.a;
    
    let b = JSON.parse(JSON.stringify(obj));
    // Uncaught TypeError: Converting circular structure to JSON
复制代码

new Date 情况下,转换结果不正确。(这个函数还不是会很用不是很了解)

四、总结

... 和原数据是否指向同一对象 第一层数据为基本数据类型 原数据中包含子对象
赋值 改变会使原数据一同改变 改变会使原数据一同改变
浅拷贝 改变不会使原数据一同改变 改变会使原数据一同改变
深拷贝 改变不会使原数据一同改变 改变不会使原数据一同改变
文章分类
前端
文章标签