拷贝是啥?
顾名思义也就是复制,我们知道在JavaScript中基本数据类型都是保存在栈中的,而复杂数据类型(object)是保存在堆中的,在栈中不过是保存了堆的地址(引用)。
浅拷贝与深拷贝
浅深拷贝都是对引用类型的数据而言的,基本数据类型一赋值就开辟了独立的栈空间,互不影响。
- 浅拷贝:当我们对基本数据类型复制,会把值全部复制过去的。如果是引用类型,也是把值复制过去,不过这个值是地址引用。这样如果其中一个对象改变了这个地址,就会影响到另一个对象。
- 深拷贝:是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象。
1 2 3 4 5 6 7 8
| let a = 1 let b = a b = 2
let a = [1,2,3] let b = a b[0] = 2
|
赋值&&浅拷贝&&深拷贝的区别
赋值:只是在栈中新建一个变量,指向同一个堆内存,也就是把地址复制过来。
浅拷贝:会新建个一对象,如果属性是基本类型,则拷贝基本数据类型的值,如果是引用数据类型,则拷贝内存地址。因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。
深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响。
来看看下面的例子1 2 3 4 5 6 7 8 9
| let a = { name:'lisi', hobby:[1,2,[3,4],5,6] } let b = a b.name = 'zhangsan' b.hobby = [3,4,10]
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| let a = { name:'lisi', hobby:[1,2,[3,4],5,6] } let b = shallowClone(a) b.name = 'zhangsan' b.hobby[1] = 10
function shallowClone(source) { var target = {}; for(var i in source) { if (source.hasOwnProperty(i)) { target[i] = source[i]; } } return target; }
|
1 2 3 4 5 6 7 8 9 10
| let a = { name:'lisi', hobby:[1,2,[3,4],5,6] } let b = JSON.parse(JSON.stringify(a)) b.name = 'zhangsan' b.hobby[1] = 10
|
浅拷贝实现
Object.assign()
Object.assign()
方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign()
进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
1 2 3 4 5 6
| let obj1 = {person:{name:'lisi',age:18},money:666} let obj2 = Object.assign({},obj1) obj2.person.age = 20 obj2.money = 777 console.log(obj1) console.log(obj2)
|
注意:当object只有一层的时候,是深拷贝
Array.prototype.concat()
1 2 3 4 5 6
| let arr1 = [1,2,{name:'lisi',age:18},3,4] let arr2 = arr1.concat() arr2[1] = 10 arr2[2].name = 'zhangsan' console.log(arr1) console.log(arr2)
|
Array.prototype.slice()
1 2 3 4 5 6
| let arr1 = [1,2,{name:'lisi',age:18},3,4] let arr2 = arr1.slice() arr2[1] = 10 arr2[2].name = 'zhangsan' console.log(arr1) console.log(arr2)
|
展开运算符…
1 2 3 4 5 6
| let obj1 = {person:{name:'lisi',age:18},money:666} let obj2 = {...obj1} obj2.person.age = 20 obj2.money = 777 console.log(obj1) console.log(obj2)
|
函数库lodash的_.clone方法
1 2 3 4 5 6
| let obj1 = {person:{name:'lisi',age:18},money:666} let obj2 = _.clone(obj1) obj2.person.age = 20 obj2.money = 777 console.log(obj1) console.log(obj2)
|
深拷贝实现
JSON.parse
1 2 3 4 5 6 7 8 9 10
| let a = { name:'lisi', hobby:[1,2,[3,4],5,6] } let b = JSON.parse(JSON.stringify(a)) b.name = 'zhangsan' b.hobby[1] = 10
|
这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于JSON.stringify
和JSON.parse
处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null
)了。
jQuery.extend()方法
jquery 有提供一個$.extend可以用来做 Deep Copy;$.extend(deepCopy, target, object1, [objectN])//第一个参数为true,就是深拷贝
1 2 3
| let obj1 = {person:{name:'lisi',age:18},money:666} let obj2 = $.extend(true,{},obj1) console.log(obj1.person === obj2.person)
|
函数库lodash的_.cloneDeep方法
1 2 3
| let obj1 = {person:{name:'lisi',age:18},money:666} let obj2 = _.cloneDeep(obj1) console.log(obj1.person === obj2.person)
|
手写深拷贝
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| function deepClone(obj){ if(obj === null) return obj if(typeof obj !== 'object') return obj if (obj instanceof Date) return new Date(obj); if (obj instanceof RegExp) return new RegExp(obj); let cloneObj = new obj.constructor(); for(let key in obj){ if(obj.hasOwnProperty(key)){ cloneObj[key] = deepClone(obj[key]); } } return cloneObj; }
|