存储位置
在JS中,对于简单数据类型,比如number、null、undefined、string、boolean来说
它们的值是存在栈中的
而对于复杂数据类型,比如Object(array,function,Date)等,它的具体内容是存放在堆中的,在栈中存放指向该引用类型的地址
赋值
知道了它存储的位置,对于赋值操作也就不难理解了
1 2 3 4 5 6
| let arr = [1,2,3]; let arr2 = arr; console.log(arr2); arr2[1] = 9; console.log(arr); console.log(arr2);
|
正是由于它们存储的是同一个地址,当改变其中一个副本的值的时候,另一个值也会随之改变
浅拷贝
数组浅拷贝
数组浅拷贝有许多原生的方法
比如:slice方法、concat方法、Array.from()等
1 2 3 4 5 6 7 8 9
| let arr = [1, [2, 3], 4]; let arr1 = arr.slice(0); let arr2 = [].concat(arr); let arr3 = Array.from(arr); arr2.push(9); arr2[1].push(6); console.log(arr1); console.log(arr2); console.log(arr3);
|
值得注意的是,ES6中的扩展运算符对于一维数组可以实现深拷贝,但是对于多维数组只能实现浅拷贝,对于对象同样如此
1 2 3 4 5 6 7 8 9 10
| let arr = [1,2,3]; let arr2 = [...arr]; arr2.push(6); console.log(arr2); console.log(arr); let arr3 = [1,[3,4],2]; let arr4 = [...arr3]; arr4[1][0] = 8; console.log(arr3); console.log(arr4);
|
对象浅拷贝
1 2 3 4 5 6 7 8 9
| let obj = { name:"zhangsan", hobby:['杀人','放火'] } let obj2 = Object.assign({},obj); obj2.hobby[0] = '打游戏'; obj2.name = 'lisi'; console.log(obj); 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
| function shallowCopy(obj){ let newobj = Array.isArray(obj)?[]:{} for(let i in obj){ if(obj.hasOwnProperty(i)){ newobj[i] = obj[i] } } return newobj } let obj = { name:"zhangsan", hobby:['杀人','放火'] } let arr = [1,[2,3],4]; let newarr = shallowCopy(arr); newarr[1][0] = 5; console.log(arr); console.log(newarr); let obj3 = shallowCopy(obj); obj3.hobby[1] = '睡觉' console.log(obj); console.log(obj3);
|

深拷贝
JSON.parse(JSON.stringify())
这是最简单也是最常用的一种方法
缺点:undefined、function、symbol会在转换过程中被忽略
1 2 3 4 5 6 7 8
| let obj = { name:"zhangsan", hobby:['杀人','放火'] } let obj2 = JSON.parse(JSON.stringify(obj)); obj2.hobby[0] = '学习'; console.log(obj); 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 32 33 34 35
| function deepClone(obj) { let type = Object.prototype.toString.call(obj).slice(8, -1); if (type === 'Object') { let newobj = {}; for (let i in obj) { if (obj.hasOwnProperty(i)) { newobj[i] = deepClone(obj[i]) } } return newobj; } if (type === 'Array') { let newarr = []; for (let i in obj) { if (obj.hasOwnProperty(i)) { newarr[i] = deepClone(obj[i]) } } return newarr; } return obj; } let obj = { name: "zhangsan", hobby: ['杀人', '放火'] } let obj2 = deepClone(obj); let arr = [1, 2, 3, [4, 5, 6]] let arr2 = deepClone(arr); arr2[3][1] = 8; obj2.hobby[0] = '学习'; console.log(arr); console.log(arr2); console.log(obj); console.log(obj2);
|

然而实际上仍然存在一些问题,比如循环引用导致的爆栈以及Symbol类型的处理等(看以后是否填坑吧)
第三方插件
比如lodash的深拷贝