谈谈深浅拷贝的问题(1)
这里先来整理一下对象的拷贝
我们都知道,对象是个复杂类型,这个变量就只是个指针。举个例子
var a=new String("abc") var b=new String("abc") console.log(a==b) //false
变量a和变量b是不相等的,因为他们指向的不是同一个对象。
console.log({}=={}) //false console.log({a:1}=={a:1}) //false
对象的复制操作呢?可以直接变量赋值么?
var a={age:18} var b=a console.log(b) // {age:18} console.log(a==b) //true
注意看,这里是不是很奇怪,刚刚我们操作console.log({a:1}=={a:1})结果是false 现在 console.log(a==b),结果居然是true.
这说明什么?这表示变量a和变量b指向的都是同一个内存地址,就是存放{a:1}的地址。
b.age=20 console.log(b.age) //20 console.log(a.age) //20
当我们改变了b.age的值之后,果然a.age的值也发生了变化。很明显,这样子的拷贝方式不能接受
直接复制不可取,那换一种方式呢?我们定义一个函数,循环的遍历一下。
function copy(obj){ var res={} for(var i in obj){ if(obj.hasOwnProperty(i)){ res[i]=obj[i] } } return res } var obj1={age:18} var obj2=copy(obj1) console.log(obj2) //{age:18}
我们对这个返回的新数操作一下,看看结果如何
obj2.age=20 console.log(obj1.age) //18 console.log(obj2.age) //20
这样子看起来,改变了obj2的 a 属性的值,obj1的 a 的的属性的值没有变化,这样子是不是就可以了呢?
我们把这个obj1数组再变化一下。
obj1 = { age: 18, position: ["北京", "广州", "上海"], name: { first: "lily", last: "lucy" } }
此时的onj1不再是一个简单的对象了,他的属性有的是数组,有的是对象 。再操作一下
obj2.position[0]="深圳" console.log(obj1.position) //["深圳","广州","上海"] console.log(obj2.position) //["深圳","广州","上海"]
我们改变了obj2的 position[0]的值,把 “北京” 变成了 “深圳” ,结果,obj1和obj2都发生了改变。
那么问题来了,为什么改变 age 和改变 position 的结果是不一样呢?来看看对象obj1
var obj1 = { age: 18, position: ["北京", "广州", "上海"], name: { first: "lily", last: "lucy" } }
当我们循环遍历对象a的属性。分开来分析
第一步,遍历属性age,把这个age赋值到obj2对象中去, 由于age就是个基本类型的变量,值为18,这就相当于 var a=18 b=a,因此obj2中的age保存的就是18
这里要和 var a={age:18} b=a 区别开来,这里的赋值是b=a, 是把 a赋值给b这是一个引用 ,而上面是把 obj1 的属性age 赋值给 obj2的属性age,这个age就是一个基本类型18
你要分清楚,赋过去的值,到底是一个基本类型还是一个引用
第二步 ,把obj1的position属性赋值给obj2,position指向的是一个数组,也就是说,传递给obj2的position和obj1的position指向的都是同一个地址.
同理,当我们赋值name属性的时候,这个属性是对一个对象的引用,也是一个指针。
我们来尝试改进一下,把 position 和 name:这两个引用属性,再调用这个函数操作,可否?
function find(copy,orig) { var copy=copy||{} for (var i in orig) { if (typeof orig[i] === 'object') { copy[i] = orig[i].constructor === Array ? [] : {} find(copy[i],orig[i]) }else{ copy[i]=orig[i] } } } var obj1 = { age: 18, position: ["北京", "广州", "上海"], name: { first: "lily", last: "lucy" } } var obj2={sex:"girl"} find(obj2,obj1)
再来测试一下,看看这个函数能否满足深复制的要求
obj2.name.first="mark"
console.log(obj1.name) //{first: "lily", last: "lucy"}
console.log(obj2.name) //{first: "mark", last: "lucy"}
obj2.position[0]="海南" console.log(obj1.position) //["北京", "广州", "上海"] console.log(obj2.position) //["海南", "广州", "上海"]
可以看到,这样子复制是完成了的.我们再来测试一下。多包裹几层,看看是否可以
var obj1 = { age: 18, position: ["北京", "广州", "上海"], name: { first:{ a:1, b:2 }, last: "lucy" } } var obj2={sex:"girl"} find(obj2,obj1) obj2.name.first.a=7 console.log(obj1.name.first) //{a: 1, b: 2} console.log(obj2.name.first) //{a: 7, b: 2}
由于是递归的调用了这个拷贝函数,无论你的对象嵌套的层次有多么的深,总是可以保证拷贝成功的。
这里还有一个问题需要注意的,放到下一章节,点击这里查看下一章节