深拷贝&&浅拷贝

4/7/2021 Javascript

# 深拷贝&&浅拷贝

# 赋值

赋值是一个变量赋给另一个变量的过程,分为两部分

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

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

let data1 = 'april'
let data2 = data1

console.log(data2) // april
data1 = 'may'
console.log(data1) // may
console.log(data2) // april
1
2
3
4
5
6
7

对引用类型进行 赋址操作,两个变量指向同一个对象,改变第一个变量 data1 也会影响另一个变量 data2,哪怕改变的只是对象 data1 的基本类型数据。

let data1 = {
    name: 'april',
    detail: {
        age: 24,
        address: '陕西',
    },
}
let data2 = data1

data1.name = 'may'
data1.detail.age = 26
console.log(data1)
// {
//     detail: {age: 26, address: "陕西"}
//     name: "may"
// }
console.log(data2)
// {
//     detail: {age: 26, address: "陕西"}
//     name: "may"
// }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

日常开发中,并不希望改变变量 data1 之后影响到变量 data2,这就需要用到深拷贝和浅拷贝

# 浅拷贝

# 1. 浅拷贝概念

创建一个对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。 简单理解:浅拷贝只解决了第一层的问题,拷贝第一层的基本类型值,以及第一层的引用类型地址。

# 2. 浅拷贝使用场景

  • Object.assign()

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

let data1 = {
    name: 'april',
    detail: {
        age: 24,
        address: '陕西',
    },
}

let data2 = Object.assign({}, data1)
console.log(data2)
// {
//     detail: {age: 24, address: "陕西"}
//     name: "april"
// }
data1.name = 'may'
data1.detail.age = 26
console.log(data1)
// {
//     detail: {age: 26, address: "陕西"}
//     name: "may"
// }
console.log(data2)
// {
//     detail: {age: 26, address: "陕西"}
//     name: "april"
// }
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

从打印的值我们可以看到,改变对象 data1 之后,对象 data2 的基本属性不变,当改变对 data1 中的对象 detail 时,对象 data2 相应的属性值也发生了变化。

  • 展开语法 Spread
let data1 = {
    name: 'april',
    detail: {
        age: 24,
        address: '陕西',
    },
}

let data2 = { ...data1 }
console.log(data2)
// {
//     detail: {age: 24, address: "陕西"}
//     name: "april"
// }
data1.name = 'may'
data1.detail.age = 26
console.log(data1)
// {
//     detail: {age: 26, address: "陕西"}
//     name: "may"
// }
console.log(data2)
// {
//     detail: {age: 26, address: "陕西"}
//     name: "april"
// }
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

通过打印的结果来看,它的效果和**Object.assign()**是一样的

  • Array.prototype.slice()

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

let arr1 = [0, 1, [2, 3]]
let arr2 = arr1.slice(1)
console.log(arr2) // [1, [2, 3]]
arr1[1] = 99
arr1[2][0] = 44
console.log(arr1) // [0, 99, [44, 3]]
console.log(arr2) // [1, [44, 3]]
1
2
3
4
5
6
7

更改 arr1[2][0]之后,arr2 的值也发生了改变,说明 slice()方法是浅拷贝,相应的还有 concat 等,在日常开发中面对复杂数组结构要额外注意。

# 深拷贝

# 1. 深拷贝概念

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

# 2. 深拷贝使用场景

  • JSON.parse(JSON.stringify(object))
let data1 = {
    name: 'april',
    detail: {
        age: 24,
        address: '陕西',
    },
}

let data2 = JSON.parse(JSON.stringify(data1))
console.log(data2)
// {
//     detail: {age: 24, address: "陕西"}
//     name: "april"
// }
data1.name = 'may'
data1.detail.age = 26
console.log(data1)
// {
//     detail: {age: 26, address: "陕西"}
//     name: "may"
// }
console.log(data2)
// {
//     detail: {age: 24, address: "陕西"}
//     name: "april"
// }
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

根据打印可以看到,完全改变 data1 之后,对 data2 没有任何影响,这就是深拷贝的作用。

那通过该方法对数组拷贝是什么效果呢。

let arr1 = [0, 1, [2, 3]]
let arr2 = JSON.parse(JSON.stringify(arr1.slice(1)))
console.log(arr2) // [1, [2, 3]]
arr1[1] = 99
arr1[2][0] = 44
console.log(arr1) // [0, 99, [44, 3]]
console.log(arr2) // [1, [2, 3]]
1
2
3
4
5
6
7

可以看到,对数组进行深拷贝之后,改变原数组不会影响到拷贝之后的数组。

但是该方法会存在以下问题:

存在原因:JSON 是一种语法,用来序列化对象、数组、数值、字符串、布尔值和 null。

查看 MDN 对于 JSON 的解释 (opens new window)

1、会忽略 undefined、symbol、函数

let obj = {
    name: 'april',
    a: undefined,
    b: Symbol('april'),
    c: null,
    d: function() {},
}
let result = JSON.parse(JSON.stringify(obj))
console.log(result)
// {
//     c: null,
//     name: 'april',
// }
1
2
3
4
5
6
7
8
9
10
11
12
13

2、循环引用的对象会报错

let obj = {
    a: 1,
    b: {
        c: 2,
        d: 3,
    },
}
obj.a = obj.b
obj.b.c = obj.a
let result = JSON.parse(JSON.stringify(obj))
// VM2796:10 Uncaught TypeError: Converting circular structure to JSON
// --> starting at object with constructor 'Object'
1
2
3
4
5
6
7
8
9
10
11
12

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

new Date()
// Wed Apr 07 2021 12:01:48 GMT+0800 (中国标准时间)

JSON.stringify(new Date())
// ""2021-04-07T04:02:06.687Z""

JSON.parse(JSON.stringify(new Date()))
// "2021-04-07T04:02:25.856Z"
// 可以转成时间戳
let date = new Date().valueOf()
let result = JSON.parse(JSON.stringify(date))
1
2
3
4
5
6
7
8
9
10
11

4、不能正确处理正则

let obj = {
    name: 'april',
    reg: /123/,
}
let result = JSON.parse(JSON.stringify(obj))
console.log(result)
// {
//     name: 'april'
//     reg: {},
// }
1
2
3
4
5
6
7
8
9
10
上次更新: 4/7/2021, 4:32:49 PM