一.前传
既然是要拷贝数据那就得了解数据类型, JS中的几种数据类型,分别有String、Number、Boolean、undefined、null、Object,ES6还多了一种symbol,ES10新增的BigInt(任意精度整数)。
String、Number、Boolean、undefined、null属于基本数据类型,Object则是属于复杂数据类型,比如数组Array,日期date,正则reg等等都是属于Object。
大学学的数据结构告诉我们简单数据类型在内存中存在栈内存中,每个被声明的简单数据类型都有他自己的一块内存空间;复杂数据类型在内存中的声明存放在栈内存中注意存放的只是一个地址,但这个地址指向堆内存,堆内存存的是复杂数据类型的数据。
怎么区分深拷贝与浅拷贝,假设B复制了A,当修改B时,看A是否会发生变化,如果A跟着变了,说明这是浅拷贝;如果A不变则是深拷贝。
复杂数据类型
1 var arr1 = [1,2,3]; 2 3 var arr2 = arr1; //[1,2,3] 4 5 arr2 .push(4) //[1,2,3,4] 6 7 arr1 //[1,2,3,4]
解释一下:arr1被分配到了堆内存中,在栈内存留下可以寻找到的指针,也就是说当我们创建新数组arr2时,赋予arr2的是arr1在栈中的地址(指针),其实仍与旧数组arr1共享同一个内存,所以修改新数组arr2后,旧数组arr1也会被修改。
基本数据类型
1 var a = 1; 2 3 var b = a; //1 4 5 b = 2; 6 7 a = 1
解释一下:出现的情况跟上面为什么值没有被修改,因为上面代码中的a,b只是基本类型,声明后都是保存在栈内存中,值虽然相同,但是两个相互独立,修改互不影响。
总结:①当基本数据类型实现浅拷贝,由于数据存放在栈内存中相互独立互不影响;②复杂类型实现浅拷贝,新对象与原对象指向堆内存同一地址所以相互影响。
二.怎么实现深拷贝
我们发现复杂类型的浅拷贝藕断丝的场景让我们在处理数据上很麻烦,是时候认识深拷贝了,它可以将复杂类型的数据相互独立出来,互不影响。
1.采用最基本的递归方式去遍历数据
1 function copy(obj) { 2 if(typeof obj == "object") { //判断是否复杂类型 3 var result = obj.constructor == Array ? [] : {};//判断数组类型或是object,数组即result=[],object即result={} 4 for(let i in obj) { 5 result[i] = typeof obj[i] == "object" ? copy(obj[i]) : obj[i]//判断数据每一项是否是object 6 } 7 } else { 8 var result = obj //基本类型直接拷贝 9 } 10 return result; 11 }
注意:深拷贝,是拷贝对象各个层级的属性,对于一些层级比较深的对象只拷贝第一层并不能实现深拷贝。
2.通过JSON对象实现深拷贝
1 function deepClone(obj) { 2 var obj = JSON.stringify(obj), 3 objClone = JSON.parse(obj); 4 return objClone; 5 }
但是吧这个方式只能实现对数据的深拷贝,对于对象中的方法却无能为力。
3.jQuery的extend方法实现深拷贝
1 var array = [1,2,3,4]; 2 var newArray = $.extend(true,[],array); // true为深拷贝,false为浅拷贝
扩展:js数组API
这是一篇写js数组api方法,里边的那些不改变原数组的api可以实现对基本数据类型的深拷贝,但是对于复杂数据类型只能深拷贝其第一层数据并不能真正实现深拷贝。