深拷贝&&浅拷贝
# 深拷贝&&浅拷贝
# 赋值
赋值是一个变量赋给另一个变量的过程,分为两部分
- 基本数据类型:赋值,赋值之后两个变量互不影响
- 引用数据类型:赋 址,两个变量具有相同的引用,相互之间有影响
基本类型进行赋值操作,两个变量互不影响
let data1 = 'april'
let data2 = data1
console.log(data2) // april
data1 = 'may'
console.log(data1) // may
console.log(data2) // april
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"
// }
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"
// }
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"
// }
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]]
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"
// }
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]]
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',
// }
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'
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))
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: {},
// }
2
3
4
5
6
7
8
9
10