浅拷贝拷贝的是引用地址,深拷贝拷贝的是值。基本数据类型进行的都是深拷贝。
我们在上一篇说过,Object.assign()
可以实现对象的拷贝与合并。但是 Object.assign() 方法只能实现浅拷贝。我们看一下例子:
1 2 3 4 5 6 7 8 9 10 11
| let target = {} let source = { a: { b: { c: 1 }, e: 2, f: 3 } } Object.assign(target, source)
|
以上代码可以将 sourse 拷贝给 target。我们也说过,如果出现了相同的属性名,后面的会将前面的覆盖。
1 2 3 4 5 6 7 8 9 10 11 12 13
| let target = { a: { e: 4, f: 5 } } let source = { a: { e: 2 } } Object.assign(target, source) console.log(target)
|
此时,target 与 source 里都有属性 a,source 的 a 会覆盖 target 的 a。但是拷贝过去的是 a 的引用地址,而不是a的值。因此 target 的输出结果是 { a: { e:2 } }。如果覆盖过去的是值,那么 source 里 a 的 e 属性会覆盖掉 target 里 a 的 e 属性,并保留 target 里 a 的 f 属性,那么 target 应该是 { a: { e:2, f:5 } }。实际上,是将 source 里 a 保存的对象的引用地址复制给了 target 里的 a。因此 Object.assign() 方法实现的是浅拷贝。
我们可以使用JSON.stringify()
与JSON.parse()
实现深拷贝。
1 2 3 4 5
| let obj1 = { name: 'lily' } let str = JSON.stringify(obj1) let obj2 = JSON.parse(str)
|
此时就实现了 obj2 对 obj1 的深拷贝,它们指向的不是同一个对象。但是这种方法并不能拷贝对象里的方法。
1 2 3 4 5 6 7 8 9 10
| let obj1 = { name: 'lily', Hi(){ console.log('Hi') } } let str = JSON.stringify(obj1) let obj2 = JSON.parse(str) console.log(obj2)
|
我们再来手写一个深拷贝函数:
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
| let checkType = data => { return Object.prototype.toString.call(data).slice(8, -1) }
let deepClone = target => { let targetType = checkType(target) let result if (targetType === 'Object') { result = {} } else if (targetType === 'Array') { result = [] } else { return target } for (let i in target) { let value = target[i] let valueType = checkType(value) if (valueType === 'Object' || valueType === 'Array') { result[i] = deepClone(value) } else { result[i] = value } } return result }
let arr1 = [1, 2, {age: 18}] let arr2 = deepClone(arr1) arr2[2].age = 34 console.log(arr1)
|
deepClone 函数可以对 对象、数组、基本数据类型 实现深拷贝,也可以拷贝对象里的方法。