曾经,我非常羡慕那些人见人爱的人,我也想要变成那样,可是后来我才明白人见人爱也是需要天赋的,后来我开始默默努力,我想,就算我不能让每个人都喜欢我,至少因为我做的努力能得到别人的尊重。

Javascript中的深拷贝和浅拷贝

文章目录

  1. JavaScript中的变量类型
  2. 深拷贝和浅拷贝的理解
  3. 深拷贝和浅拷贝的实现方式
  4. 为什么需要深拷贝和浅拷贝

 

JavaScript中的变量类型

(1)、基本类型

  JavaScript中的基本类型有五种: null、undefined、boolean、string、number。 变量是按值存放的,存放在栈中的简单数据段,可以直接访问。 

(2)、引用类型

  引用类型包括对象和数组,其存储在堆当中,而变量是指针,指向堆。 当我们访问的时候,实际上是访问指针,然后指针去寻找对象或数组。 

 

深拷贝与浅拷贝的理解

(1)、深拷贝

  先新建一个空对象,内存中新开辟一块地址,把被复制对象的所有可枚举的(注意可枚举的对象)属性方法一一复制过来,注意要用递归来复制子对象里面的所有属性和方法,直到子子.....属性为基本数据类型。关键点:开辟新内存、递归复制。 

  另外一种定义:深拷贝指的是对象属性所引用的对象全部进行新建对象复制,以保证深复制的对象的引用图不包含任何原有对象或对象图上的任何对象,隔离出两个完全不同的对象图。

 

(2)、浅拷贝

  一个对象复制另外一个对象,如果不是深拷贝,就是浅拷贝。 简单地说,浅拷贝就是将一个对象的内存地址的“”编号“”复制给另一个对象。即在真正访问的时候还是会访问到被复制对象。 或者只是深拷贝了第一层的引用类型,而没有拷贝更深层次的应用类型,而是利用复制地址的方式,这也是浅拷贝。 

 

 

深拷贝和浅拷贝的实现方式

 

浅拷贝

浅拷贝1

        var obj1 = {
            name: 'wayne'
        }
        var obj2 = obj1
        obj2.name = 'hedy'
        console.log(obj1.name) // 'hedy'

  这里首先创建了一个 obj1 对象,然后将obj复制给了 obj2, 但是这里仅仅是指针的复制,所以在修改 obj2.name 的时候,实际上是修改的同一个堆中的对象,既浅拷贝。 

 

 

 

浅拷贝2

       var obj = {
            a: 1,
            b: {
                d: {
                    e: 'test'
                }
            },
            c: [1, 2, 3]
        }

        function shallowClone1(copyObj) {
            var newObj = {};
            for (var prop in copyObj) {
                newObj[prop] = copyObj[prop];
            }
            return newObj;
        }

        var newObj = shallowClone1(obj); 

        console.log(newObj.b.d === obj.b.d); // true

  即通过for in的形式将对象进行复制,这里可以看到复制只是对于指针的复制,得到的新的对象还是指向同一个堆中的对象,所以是浅复制。

 

 

浅拷贝3

Object.assign()

        var obj1 = {
            name: 'wayne', 
            age: 22,
            other: {
                hobby: 'table',
                school: 'xjtu'
            }
        }
        var obj2 = Object.assign({}, obj1);
        obj2.name = 'hedy'
        console.log(obj1.name) // wayne

        obj2.other.hobby = 'sing'
        console.log(obj1.other.hobby) // sing

只从表面上来看,似乎Object.assign()的目标对象是{},是一个新的对象(开辟了一块新的内存空间),是深拷贝。 

当我们修改obj2.name的时候,obj1.name没有改变,但是当我们修改 obj2.other.hobby 的时候,obj1.other.hobby 同样也发生了变化。 

即Object.assign()也是浅拷贝,或者说只是深拷贝了第一层,这样我们认为它还是浅拷贝。 

 

 

浅拷贝4

concat

        var a = [2, [3,5,7], {name: 'wayne'}];
        var b = a.concat(4)
        a[0] = 3;
        console.log(b[0]) // 2 看起来像深复制
        a[1][0] = 666; 
        console.log(b[1][0]) // 666 浅复制
        a[2].name = 'hedy'
        console.log(b[2].name) // hedy 浅复制

 

可以看到通过concat返回的新数组,只有改变其中一个的布尔值、字符串、数值,另一个不会改变,但是改变其中的对象、数组时,可以发现,另一个也在同时改变,即还是引用原来的堆中的内容。

 

slice

        var a = [2, [3,5,7], {name: 'wayne'}];
        var b = a.slice(0)
        a[0] = 3;
        console.log(b[0]) // 2 看起来像深复制
        a[1][0] = 666; 
        console.log(b[1][0]) // 666 浅复制
        a[2].name = 'hedy'
        console.log(b[2].name) // hedy 浅复制

 

这段代码仅仅是将上一段中的concat修改为了slice,发现结果也是一样的,即slice方法得到的也是浅复制。

 

 

 

 

深拷贝

 

深拷贝1

JSON.stringify() 和 JSON.parse() 

        var obj1 = {
            name: 'wayne', 
            age: 22,
            other: {
                hobby: 'table',
                school: 'xjtu'
            }
        }
        var obj2 = JSON.parse(JSON.stringify(obj1));
        obj2.name = 'hedy'
        console.log(obj1.name) // wayne

        obj2.other.hobby = 'sing'
        console.log(obj1.other.hobby) // table

 

可以看出通过JSON.stringify先将对象转化为字符换,然后再通过JSON.parse()转化为对象,这个对象就是完全在开辟的新的内存空间中的对象 。

 

 

深拷贝2

jquery $.clone(true) / $.extend(true)

        var x = {
            a: 1,
            b: { f: { g: 1 } },
            c: [ 1, 2, 3 ]
        };

        var y = $.extend({}, x),          //浅复制
            z = $.extend(true, {}, x);    //深复制

        console.log(y.b.f === x.b.f )    // true
        console.log(z.b.f === x.b.f)     // false

 

可以看到通过 $.extend() 传入第一个参数 true, 就可以进行深复制了。

 

深拷贝3


下面使用递归的方法进行深拷贝还是不错的。

// 新开辟一块内存空间,把源对象的属性和方法逐个拷贝进来。
function deepClone (obj) {
  let temp = Array.isArray(obj) ? [] : {}
  for (let key in obj) {
    //是否有嵌套对象
    temp[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
  }
  return temp
}
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>deepClone</title>
</head>
<body>
  <script>
    function deepClone(obj) {
      var temp = Array.isArray(obj) ? [] : {};
      for (var key in obj) {
        temp[key] =  typeof obj[key] === 'object' ? deepClone(key) : obj[key]
      }
      return temp;
    }

    var foo = deepClone({
      name: 'wayne', 
      age: 21, 
      others: {
        school: 'xjtu', 
        province: 'xinjiang'
      }
    })

    console.log(foo);
  </script>
</body>
</html>

 

 

深拷贝4(推荐)

function deepClone(source) {
  if(!source) return console.error('please verify your source!');
  var target;
  if(JSON){
    target = JSON.parse(JSON.stringify(source));
  }else if(typeof(source) === 'object') {
    target = source instanceof Array ? [] : {};
    for(var key in source) {
       source.hasOwnProperty(key) && (target[key] = deepClone(source[key]));
    }
  }else{
    target = source;
  }
  return target;
}

这里的方法比较巧妙。 

即首先判断source是否合理,然后如果支持JSON(IE8以下不支持),我们就使用JSON的序列化、反序列化实现深拷贝,如果不支持,我们也可以先判断对象中是否有该属性,然后如果有,就利用递归进行,所以在最后添加 target = source ,就是为了解决递归的问题的。

 

 

 

 

 

 

 

推荐文章:https://www.zhihu.com/question/23031215

       http://blog.csdn.net/waiterwaiter/article/details/50267787 

posted @ 2017-08-15 12:51  Wayne-Zhu  阅读(1055)  评论(0编辑  收藏  举报

一分耕耘,一分收获。