对于基本数据类型,没有深浅拷贝的区别。深浅拷贝只针对引用数据类型。 如果我们要赋值对象的所有属性都是引用类型可以用浅拷贝
# 概念
- 浅拷贝:只复制一层对象,当对象的属性都是引用类型时,实质复制的是其引用,当引用值发生改变时,也会跟着改变
- 深拷贝:另外申请一块内存,内容跟原对象一样,更改原对象,不会改变拷贝对象
# 原因
- 基本数据类型存在栈
stack
, 栈内存上的分配的空间生命周期很短,方法执行完毕就被释放掉 - 引用数据类型存在堆内存
heap
, js不允许直接访问堆内存中的位置,不能直接操作对象的堆内存空间- 读取对象时,实际上是在操作对象的引用地址而不是实际的对象
- 引用地址保存在栈内存中
# 实现
浅拷贝
for-in
遍历let copyObj = function(obj) { let rst = {} for (let key in obj) { if (obj.hasOwnProperty(key)) { rst[key] = obj[key] } } return rst } let obj1 = { name: '迪丽热巴', sex: 'female', friend: { name: 'Ethan' } } let obj2 = copyObj(obj1) obj2.name = '古力娜扎' obj2.friend.name = 'FU' console.log(obj1.friend.name) // FU
Object.assign(target, source)
let obj1 = { name: '迪丽热巴', sex: 'female', friend: { name: 'Ethan' } } let obj2 = Object.assign({}, obj1) obj2.name = '古力娜扎' obj2.friend.name = 'FU' console.log(obj1.friend.name) // FU
...
拓展运用符let obj1 = { name: '迪丽热巴', sex: 'female', friend: { name: 'Ethan' } } let obj2 = {...obj1} obj2.name = '古力娜扎' obj2.friend.name = 'FU' console.log(obj1.friend.name) // FU
slice
(适用于数组)let a = [1, 2, 3, {name: 'Ethan'}] let b = a.slice() b[0] = 4 b[3].name = 'Linna' console.log(a, b) // a [1, 2, 3, {name: 'Linna'}] // b [4, 2, 3, {name: 'Linna'}]
深拷贝
JSON.parse(JSON.stringify(obj))
let obj1 = { name: 'Ethan', car: ['BYD', 'BMW'] } let obj2 = JSON.parse(JSON.stringify(obj1)) obj1.name = 'Linna' obj2.car[0] = 'BENZ' console.log(obj1, obj2) // obj1 {name: 'Ethan',car: ['BYD', 'BMW']} obj1.car没有改变,说明实现了深拷贝 // obj2 {name: 'Linna',car: ['BENZ', 'BMW']} // let obj1 = { name: 'Ethan', car: ['BYD', 'BMW'], hit: function() {}, age: undefined } let obj2 = JSON.parse(JSON.stringify(obj1)) console.log(obj2) /* 1. 当对象的属性值为函数或undefined,序列化结果会丢失函数或undefined 2. 对象中存在循环引用到情况无法实现深拷贝 3. Symbol不能被JSON序列化 4. RegExp/Error对象,序列化结果为空对象 5. 会丢失原型对象 */
递归实现深拷贝
let deepClone = function(obj) { let rst = Array.isArray(obj) ? [] : {} if (obj && typeof obj === 'object') { for (let key in obj) { if (obj.hasOwnProperty(key)) { if (obj[key]&& typeof obj[key] === 'object') { rst[key] = deepClone(rst[key]) } else { rst[key] = obj[key] } } } } return rst } let obj1 = { name: 'Ethan', car: ['BYD', 'BMW'], hit: function() {}, age: undefined } let obj2 = deepClone(obj1) obj2.name = 'Linna' obj2.car = ['BENZ'] console.log(obj1, obj2) // obj1 { age: undefined, car: ['BYD', 'BMW'], hit: ƒ (), name: "Ethan" } // obj2 { age: undefined, car: ['BENZ'], hit: ƒ (), name: "Linna" }
第三方库
lodash.cloneDeep
(opens new window)var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false
# 注意事项
- 是否存在循环引用?
- 传入的对象是使用哪种方式创建?
- 对象字面量创建
- 构造函数生成
- 是否要拷贝原型链上的属性
- 拷贝的话存在多个同名属性要保留哪个