当前位置:   article > 正文

详细解析赋值、浅拷贝和深拷贝的区别_赋值,浅拷贝,深拷贝的区别

赋值,浅拷贝,深拷贝的区别

一、赋值(Copy)

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

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

对基本类型进行赋值操作,两个变量互不影响。

// 木易杨
let a = "muyiy";
let b = a;
console.log(b);
// muyiy

a = "change";
console.log(a);
// change
console.log(b);
// muyiy
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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

// 木易杨
let a = {
    name: "muyiy",
    book: {
        title: "You Don't Know JS",
        price: "45"
    }
}
let b = 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: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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

二、浅拷贝(Shallow Copy)

1、什么是浅拷贝

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

在这里插入图片描述

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

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

2、浅拷贝使用场景
Object.assign()

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

有些文章说Object.assign() 是深拷贝,其实这是不正确的。

先说说asign的语法,

Object.assign(target, source)
其中target是目标,source是源,assign将source的可枚举属性拷贝到target上,target如果本身就含额外属性,则额外部分不会被删除或修改。

// 木易杨
let a = {
    name: "muyiy",
    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: "muyiy",
// 	book: {title: "You Don't Know JS", price: "55"}
// } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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

  • 展开语法 Spread
// 木易杨
let a = {
    name: "muyiy",
    book: {
        title: "You Don't Know JS",
        price: "45"
    }
}
let b = {...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: "muyiy",
// 	book: {title: "You Don't Know JS", price: "55"}
// } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

通过代码可以看出实际效果和 Object.assign() 是一样的。

  • 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]]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

三、深拷贝(Deep Copy)

1、什么是深拷贝

深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。
在这里插入图片描述

2、深拷贝使用场景

JSON.parse(JSON.stringify(object))

还是先解释基本语法,

JSON.parse(str) 【从一个字符串中解析出json对象】

例子:

//定义一个字符串

var data=’{“name”:“goatling”}’

//解析对象​

​JSON.parse(data)

结果是:

​name:“goatling”

JSON.stringify()【从一个对象中解析出字符串】

var data={name:‘goatling’}

JSON.stringify(data)

结果是:

‘{“name”:“goatling”}’

对数组深拷贝之后,改变原数组不会影响到拷贝之后的数组。

但是该方法有以下几个问题。

1、会忽略 undefined

2、会忽略 symbol

3、不能序列化函数

4、不能解决循环引用的对象

5、不能正确处理new Date()

6、不能处理正则

  • undefined、symbol 和函数这三种情况,会直接忽略。

    // 木易杨
    let obj = {
        name: 'muyiy',
        a: undefined,
        b: Symbol('muyiy'),
        c: function() {}
    }
    console.log(obj);
    // {
    // 	name: "muyiy", 
    // 	a: undefined, 
    //  b: Symbol(muyiy), 
    //  c: ƒ ()
    // }
    
    let b = JSON.parse(JSON.stringify(obj));
    console.log(b);
    // {name: "muyiy"}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
  • 循环引用情况下,会报错。

    // 木易杨
    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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
  • new Date 情况下,转换结果不正确。

    // 木易杨
    new Date();
    // Mon Dec 24 2018 10:59:14 GMT+0800 (China Standard Time)
    
    JSON.stringify(new Date());
    // ""2018-12-24T02:59:25.776Z""
    
    JSON.parse(JSON.stringify(new Date()));
    // "2018-12-24T02:59:41.523Z"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    解决方法转成字符串或者时间戳就好了。

    // 木易杨
    let date = (new Date()).valueOf();
    // 1545620645915
    
    JSON.stringify(date);
    // "1545620673267"
    
    JSON.parse(JSON.stringify(date));
    // 1545620658688
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
  • 正则情况下,

    // 木易杨
    let obj = {
        name: "muyiy",
        a: /'123'/
    }
    console.log(obj);
    // {name: "muyiy", a: /'123'/}
    
    let b = JSON.parse(JSON.stringify(obj));
    console.log(b);
    // {name: "muyiy", a: {}}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

PS:为什么会存在这些问题可以学习一下 JSON。

除了上面介绍的深拷贝方法,常用的还有jQuery.extend() 和 lodash.cloneDeep(),后面文章会详细介绍源码实现,敬请期待!

四、总结

和原数据是否指向同一对象第一层数据为基本数据类型原数据中包含子对象
赋值改变会使原数据一同改变改变会使原数据一同改变
浅拷贝改变会使原数据一同改变改变会使原数据一同改变
深拷贝改变会使原数据一同改变改变会使原数据一同改变
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/68281
推荐阅读
相关标签
  

闽ICP备14008679号