谈谈深浅拷贝的问题(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}

由于是递归的调用了这个拷贝函数,无论你的对象嵌套的层次有多么的深,总是可以保证拷贝成功的。

这里还有一个问题需要注意的,放到下一章节,点击这里查看下一章节

posted @ 2017-09-01 00:37  阿柴与米  阅读(243)  评论(0编辑  收藏  举报