关于js对象拷贝的一些思考和总结

时间:2020-12-03 19:58:55

最近在redux中,reducer处理返回状态的问题,让我着实踩了一些坑,返回的新的state却没有改变视图,让我决心研究一波js对象拷贝的问题。

在js中,值的类型分俩种,一种是基本类型的值,string,boolean,number,undefined,对于基本类型,看下面例子

let a = 1;
let b = a;
b = 2;
a //1

也就是说对于基本类型的值的引用相当于是重新赋值,也就是新建了一快内存地址,然后让b指向那块地址,b的改变不会对a产生影响

对于Array,Object引用数据类型的值,则全然不同,看例子

let a = {
  name: 'John'
}
let b = a;
b.name //John
b.name = 'Petter';
a.name // "Petter"

对于引用类型,对于b的赋值相当于是将b与a的指针指向了同一块内存,也就是对于b的修改会直接导致a也产生同样的后果,

而很多时候我们需要避免这种事情的发生,所以这个时候我们就需要通过拷贝来改变这种应用赋值。

拷贝有两种,一是浅拷贝,一种是深拷贝。

什么是浅拷贝呢?

简单讲就是只拷贝第一层,经常能看到的就是

myobj = {
  name: 'John'
}
newobj = Object.assign({},myobj);
newobj.name //John
newobj.name = 'Petter';
myobj.name // "John"
ok,如果一个对象只有一层且只有基本数据类型的话,这种方式是完全ok的,assign实现的就是在内部新建一个对象,然后将两个对象
newobj[key] = obj[key];

然后将newobj返回从而实现浅拷贝,但是这会引出一个问题,即如果这个对象不只一层呢,看下面这个 

let obj = {
  name: 'John',
  grade: {
    math:60,
  }
}
如果我们还是采用第一种方法,即浅拷贝的话,我们不难看出对于grade我们又一次将newobj.grade指向了obj.grade,他们又一次指向了相同的地址。让我们印证一下
obj={name:'John',grade:{math:60}};
newobj = Object.assign({},obj);
newobj.name = "Petter"
obj.name // "John"
newobj.grade.math //60
newobj.grade.math = 66
obj.grade.math //66

从中不难看出他们又一次指向了相同的地址,当我们改变newobj.grade.math时,obj的math也发生了改变,所以浅拷贝这时候就满足不了我们的需求了,这时候我们就需要深拷贝,关于深拷贝的实现,具体将就是对于类型的判断,如果是基本类型,ok,我们将其赋值就行,如果是引用类型,那么我们在对其进行递归处理,网上有很多,我就不献丑了,这里我想说一种黑魔法。

关于深拷贝 其中之一就是利用JSON,通过

newobj = JSON.parse(JSON.stringify(obj));

就可以实现深拷贝了,但是这种通过JSON方式实现深拷贝是有bug的,那就是他不能拷贝对象中的方法,并且会导致方法丢失,看下面栗子

obj1 = {
  name: 'John',
  getName: function() {
    return this.name;
  }
}
newobj1 = JSON.parse(JSON.stringify(obj1))
{name: "John"}name: "John"__proto__: Object

所以说这种方式在使用的时候需要小心。

ok大致就是这样啦。