js Object.assign()究竟是深拷贝还是浅拷贝
之前我有对object函数相关的常见方法做过总结,感兴趣的同学可以通过链接查看:
https://www.cnblogs.com/zhilu/p/13842177.html
一、今天对Object.assign()做一个分析总结。
1、定义:Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
2、Object.assign()拷贝
Object.assign()拷贝的是属性值。假如源对象的属性值是一个对象的引用,那么它也只指向那个引用。也就是说,如果对象的属性值为简单类型(如string, number),通过Object.assign({},srcObj);得到的新对象为深拷贝;如果属性值为对象或其它引用类型,那对于这个对象而言其实是浅拷贝的。
也就是说,当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝;
但是对象中有二级属性的时候,此方法,在二级属性以后就是浅拷贝。
3、所以,当对象中有二级属性的时候,使用递归的方式实现深拷贝
二、延伸:怎样实现深拷贝呢
1、对象深拷贝的几种方法
1)用 JSON.stringify 把对象转换成字符串,再用 JSON.parse 把字符串转换成新的对象,但是需要注意的是,可以转成 JSON 格式的对象才能使用这种方法,如果对象中包含 function 或 RegExp 这些就不能用这种方法了。
1 function deepClone2(obj) { 2 let _obj = JSON.stringify(obj), 3 return JSON.parse(_obj); 4 }
缺点:
(1)如果对象里有函数,函数无法被拷贝下来
(2)无法拷贝copyObj对象原型链上的属性和方法
(3)当数据的层次很深,会栈溢出
2)使用普通递归的方式实现深拷贝
1 function deepClone(obj){ 2 let objClone = Array.isArray(obj) ? [] : {}; 3 if (obj && typeof obj === 'object') { 4 for(let key in obj){ 5 if (obj[key] && typeof obj[key] === 'object'){ 6 objClone[key] = deepClone(obj[key]); 7 }else{ 8 objClone[key] = obj[key] 9 } 10 } 11 } 12 return objClone; 13 }
缺点:
(1)无法保持引用
(2)当数据的层次很深,会栈溢出
3)防栈溢出函数
1 function cloneLoop(x) { 2 const root = {}; 3 4 // 栈 5 const loopList = [ 6 { 7 parent: root, 8 key: undefined, 9 data: x, 10 } 11 ]; 12 13 while(loopList.length) { 14 // 深度优先 15 const node = loopList.pop(); 16 const parent = node.parent; 17 const key = node.key; 18 const data = node.data; 19 20 // 初始化赋值目标,key为undefined则拷贝到父元素,否则拷贝到子元素 21 let res = parent; 22 if (typeof key !== 'undefined') { 23 res = parent[key] = {}; 24 } 25 26 for(let k in data) { 27 if (data.hasOwnProperty(k)) { 28 if (typeof data[k] === 'object') { 29 // 下一次循环 30 loopList.push({ 31 parent: res, 32 key: k, 33 data: data[k], 34 }); 35 } else { 36 res[k] = data[k]; 37 } 38 } 39 } 40 } 41 42 return root; 43 }
优点:
(1)不会栈溢出
(2)支持很多层级的数据
1 function copyObject(orig) { 2 var copy = Object.create(Object.getPrototypeOf(orig)); 3 copyOwnPropertiesFrom(copy, orig); 4 return copy; 5 } 6 7 8 function copyOwnPropertiesFrom(target, source) { 9 Object 10 .getOwnPropertyNames(source) 11 .forEach(function (propKey) { 12 var desc = Object.getOwnPropertyDescriptor(source, propKey); 13 Object.defineProperty(target, propKey, desc); 14 }); 15 return target; 16 } 17 18 var obj = { 19 name: 'Jack', 20 age: '32', 21 job: 'developer' 22 }; 23 24 var obj2 = copyObject(obj); 25 console.log(obj2); 26 obj.age = 39; 27 obj.name = 'Tom'; 28 console.log(obj); 29 console.log(obj2);
4)通过jQuery的extend方法实现深拷贝
1 var obj = { 2 a: '123', 3 b: 234, 4 c: true, 5 d: null, 6 e: function() {console.log('test')}, 7 h: new Set([4,3,null]), 8 i:Symbol('fsd'), 9 k: new Map([ ["name", "test"], ["title", "Author"] ]) 10 } 11 console.log($.extend(true, {}, obj))
5)lodash函数库实现深拷贝
1 let clone = cloneDeep(obj)
2、数组深拷贝的几种方法
1)concat(arr1, arr2,....)
2)slice(idx1, idx2)
1)没有参数是拷贝数组
2)只有一个参数是从该位置起到结束拷贝数组元素
3)两个参数,拷贝从起始位置到结束位置的元素(不包含结束位置的元素:含头不含尾)
需要注意的是,slice()和concat()这两个方法,仅适用于对不包含引用对象的一维数组的深拷贝
三、赋值、浅拷贝与深拷贝的区别