最近在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大致就是这样啦。