第一次经手比较大型的项目,总结第一个问题:对象的深拷贝问题。之前没注意这个问题,写完代码发现总是与自己的想法不符合,经调试才发现是对象的深浅拷贝问题。
原理的话也很简单,对象作为基本类型之外的引用类型,在栈内存中保存着引用变量的名字,而这个变量保存的实际是一个地址,这个地址指向堆内存,实际这个变量的值是保存在堆内存中。
所以当我们直接用 “=” 进行变量copy时,实际copy的是对内存中的地址。copy之后改变原变量的值同样会使新变量的值进行改变。所以有时候我们需要进行深拷贝来在堆内存中开辟一个新的内存空间来放置副本,切断新旧变量之间的联系。
方法
对象的深拷贝
适用于所有类型的对象(含数组)
- JSON.parse(JSON.stringfy(Object))
**Point:**这是最简单的进行深拷贝的方法,其原理是先将其转换为JSON字符串,此时就不在存在原变量的堆地址,而是以字符串-基本变量保存在栈地址中。再将其转换回对象,则是一个完全与原变量无关的新变量。但是该方法会丢失对象所有的函数属性,undefined,以及会重置原型对象为Object(),或Array(),如果没修改多原型对象,则没有影响。
递归遍历对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14//只能clone Object和Array
function deepClone1(obj) {
//判断obj是否是引用类型
if(obj && typeof obj === "object"){
let newObj = Array.isArray(obj) ? [] : {}
for(let i in obj){
newObj[i] = deepClone1(obj[i])
}
return newObj
}else{
//如果不是引用类型则直接返回该值
return obj
}
}}
使用lodash库中的cloneDeep()函数,其原理与上述原理一样,但健壮性更高。
适用于简单对象
- Object.assign()
eg:
const obj = { a: 1 };
const copy = Object.assign({}, obj);
console.log(copy); // { a: 1 }
**Point:**该方法只适用于由基本数据类型组成的对象,不能用于对象键或值包含引用类型,否则任然是浅拷贝。同时,该函数会忽略undefined与null值(可以用来过滤无效值)
使用于简单数组
Array.slice(startPositong,endPositon)
Array.contact(arr1,arr2)
array.splice(startPositon,delteteNum,addArr1,addArr2...)
- 数组的迭代方法:
Array.every(),Array.filter(),Array.map()
**Point:**使用以上方法,必须是由基本数据类型组成的数组,数组项不能包含引用类型,否则任然是浅拷贝!!!
Final
以上方法都只会转化可枚举项,对于不可枚举项,都是默认忽略的。
综上,JSON.parse(JSON.stringfy(obj))
应该算是最简单且最易用的方法了,并且原生代码的效率相较于JS
也会有优势,所以,如果仅仅为了深拷贝,没有其他需求,该方法是比较好的方法。