深拷贝与浅拷贝的含义
首先要知道在js的世界中,有两种类型的值,分别是基础类型和引用类型。基础类型包括string,number,boolean,Symbol,undefined,null,引用类型包括array,object等。
- 基本数据类型:直接存储在栈(stack)中的数据
- 引用数据类型:在栈中存放的是指针,真实的数据存放在堆中。
首先要分清拷贝和赋值的区别
引用类型的赋值是直接赋值了一个指针,二者指向相同的数据内存。而拷贝是要创建一个新的对象/数组,如果只拷贝一层数据的话叫浅拷贝,如果拷贝多层数据叫深拷贝。
1 | let obj={ |
如何实现浅拷贝
数组的浅拷贝
slice方法
1
2
3
4let arr=[1,5,9],
arr1=arr.slice();
arr1.push(11);
console.log(arr,arr1)concat方法,当不给concat传参时,该方法和slice()作用相同
1
2let arr1=arr.concat();
...spread操作符
1
2
3
4
5
6let arr=[1,3,5,[8,9]];
let arr1=[...arr];
arr.push(15);
console.log(arr,arr1);
arr1[3].push(18);
console.log(arr,arr1);
对象的浅拷贝
手动实现
1
2
3
4
5
6
7
8
9
10function shallowCopy(src){
var target={};
for(let key in src){
if(src.hasOwnProperty(key)){
target[key]=src[key];
}
}
return target
}
// hasOwnProperty方法可以排除来自原型链上的属性Object.assign()
1
2
3
4let obj={a:{name:'weifo',age:33}};
let copyobj=Object.assign({},obj);
copyobj.a.name='curt';
concole.log(obj.a.name);//curtspread
1
2
3let obj={name:'weifo',skill:['english','code']};
let copyobj={...obj,name:'fred'};//{name:'fred',skill:[...]}
//es6语法
深拷贝的实现
trick版
1
2
3
4
5
6
7
8
9
10
11
12
13JSON.parse(JSON.stringify(obj))
// 该方法的局限性:symbol,undefined,函数的属性值会被忽略,例子如下
let syb=Symbol(12);
let obj={
name:'fred',
say:function(){
alert('It wont alert');
},
syb:syb,
age:undefined
}
let copy=JSON.parse(JSON.stringify(obj));
console.log(copy);//{name:'fred'}手动实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19function deepclone(src){
if(typeof src==='object'){
let clone=Array.isArray(src)?[]:{};
for(let key in src){
clone[key]=deepclone(src[key]);
}
return clone
}else{
return src;
}
}
let test={
name:'fred',
cities:['tokyo','paris','london']
}
let clone=deepclone(test);
clone.cities.push('rome');
clone.name='weifo';
console.log(test,clone)解决循环引用
上面的方法可以满足大部分的应用场景了,除了遇到对象引用自身的情况,如果采用上面的方法,会有爆栈的错误提示。1
2
3
4
5
6
7
8
9const test={
name:'weifo',
field:[3,5,11],
field1:{
child:'name'
}
}
test.test=test;//循环引用
deepclone(test)//RangeError
为了解决循环引用的问题,我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系,当需要拷贝对象时,先去存储对象中找,找到的话直接返回,没有则继续拷贝。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15function deepclone(target,map=new Map()){
if(typeof target==='object'){
let clone=Array.isArray(src)?[]:{};
if(map.get(target)){
return target
}
map.set(target,clone);
for(let key in target){
clone[key]=clone(target[key],map);
}
return clone
}else{
return target
}
}
- 使用lodash
1
2
3
4
5var _=require('lodash');
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);//false