深拷贝浅拷贝
Jioho 6/24/2020 Javascript
- 浅拷贝和深拷贝都只针对于
引用数据类型
,浅拷贝只复制指向某个对象的指针
,而不复制对象本身,新旧对象还是共享同一块内存 - 深拷贝会另外创造一个一模一样的对象,新对象跟原对象
不共享内存
,修改新对象不会改到原对象
;
# 区别
浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制
# 没有做处理的赋值和修改
// 对象赋值
var obj1 = {
name: 'Jioho',
age: '18',
language: [1, [2, 3], [4, 5]]
}
var obj2 = obj1
obj2.name = 'jioho'
obj2.language[1] = ['二', '三']
obj1.age = 22
console.log('obj1', obj1) // 获取的都是一样的对象。2个对象修改是同步的
console.log('obj2', obj2) // 获取的都是一样的对象。2个对象修改是同步的
console.log(obj1 === obj2) // true
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 进行浅拷贝处理
// Object.assgin() 就是浅拷贝,后续会说到
var obj1 = {
name: 'Jioho',
age: '18',
language: [1, [2, 3], [4, 5]]
}
var obj2 = Object.assign({}, obj1)
obj2.name = 'jioho'
obj2.language[1] = ['二', '三']
obj1.age = 22
console.log('obj1', obj1)
console.log('obj2', obj2)
console.log(obj1 === obj2) // false
// 第二层的属性,用全等判断还是true。说明内存地址还是一样的
console.log(obj1.language === obj2.language) // true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 进行深拷贝
// 常用的方法是JSON.stringify + JSON.parse
var obj1 = {
name: 'Jioho',
age: '18',
language: [1, [2, 3], [4, 5]]
}
var obj2 = JSON.parse(JSON.stringify(obj1))
obj2.name = 'jioho'
obj2.language[1] = ['二', '三']
obj1.age = 22
console.log('obj1', obj1)
console.log('obj2', obj2)
console.log(obj1 === obj2) // false
console.log(obj1.language === obj2.language) // false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
结论
操作 | 和原数据是否指向同一对象 | 第一层数据 为基础数据类型 | 原数据包含子对象 |
---|---|---|---|
赋值 | 是 | 改变使原数据一同改变 | 子对象一同改变 |
浅拷贝 | 否 | 第一层的原数据不变 | 原子对象会和新对象一起改变 |
深拷贝 | 否 | 原数据不变 | 原数据不变 |
# 浅拷贝有几种
# Object.assign()
Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。 但是 Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
当 object 只有一层的时候,是深拷贝
// 浅拷贝
var obj = { a: { a: 'kobe', b: 39 } }
var initalObj = Object.assign({}, obj)
initalObj.a.a = 'wade'
console.log(obj.a.a) //wade
// 深拷贝
let obj2 = {
username: 'kobe'
}
let obj3 = Object.assign({}, obj)
obj3.username = 'wade'
console.log(obj) //{username: "kobe"}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# Array.prototype.concat()
let arr = [
1,
3,
{
username: 'kobe'
}
]
let arr2 = arr.concat()
arr2[2].username = 'wade'
console.log(arr) // 修改新对象也会影响源对象
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
# Array.prototype.slice()
let arr = [
1,
3,
{
username: ' kobe'
}
]
let arr3 = arr.slice()
arr3[2].username = 'wade'
console.log(arr)
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
额外说明
Array 的 slice 和 concat 方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。 原数组的元素会按照下述规则拷贝:
如果该元素是个
对象引用(不是实际的对象)
,slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象
。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。对于
字符串
、数字
及布尔值
来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组
。
# 深拷贝
# JSON.parse(JSON.stringify())
- 优点:简单易用
- 缺点:无法处理
函数
- 原理: 用
JSON.stringify
将对象转成JSON 字符串
,再用JSON.parse()
把字符串解析成对象
,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。
# 手写递归方法
//定义检测数据类型的功能函数
function checkedType(target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
//实现深度克隆---对象/数组
function clone(target) {
//判断拷贝的数据类型
//初始化变量result 成为最终克隆的数据
let result,
targetType = checkedType(target)
if (targetType === 'Object') {
result = {}
} else if (targetType === 'Array') {
result = []
} else {
return target
}
//遍历目标数据
for (let i in target) {
//获取遍历数据结构的每一项值。
let value = target[i]
//判断目标结构里的每一值是否存在对象/数组
if (checkedType(value) === 'Object' || checkedType(value) === 'Array') {
//对象/数组里嵌套了对象/数组
//继续遍历获取到value值
result[i] = clone(value)
} else {
//获取到value值是基本的数据类型或者是函数。
result[i] = value
}
}
return result
}
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
29
30
31
32
33
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
29
30
31
32
33
# 借助第三方函数库
函数库 lodash
var _ = require('lodash')
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
}
var obj2 = _.cloneDeep(obj1)
console.log(obj1.b.f === obj2.b.f) // false
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8