web前端开发技术博客
当前位置: 前端技术 > 理解js的深拷贝和浅拷贝原理和实现的方法

理解js的深拷贝和浅拷贝原理和实现的方法

2019-05-13 分类:前端技术 作者:码云 阅读(5883)

和同事一起吃饭的时候经常大家会一起探讨一些技术上的问题,当然其中会有很多的专业性词语,比如说js的深拷贝和浅拷贝,这个名词经常听到,但有的同学可能还不是很理解他们是什么意思,也或者只是知道个皮毛而已。今天通过我在网上书籍中上学到的一些见解浅显的给大家总结了一下他们的原理以及实现方式,有不足之处欢迎留言指正。

浅拷贝

简单讲,浅拷贝就是复制一份引用,所有引用对象都指向一份数据,并且都可以修改这份数据。如下:

1
2
3
4
var m = { a: 10, b: 20 }
var n = m;
n.a = 15;
// 这时m.a的值是多少

m.a会输出15,因为这是浅拷贝,n和m指向的是同一个堆,对象复制只是复制的对象的引用。

深拷贝

深拷贝和上面浅拷贝不同,就是彻底copy一个对象,而不是copy对象的引用,例如,还是之前的例子,我们这么写:

1
2
3
var m = { a: 10, b: 20 }
var n = {a:m.a,b:m.b};
n.a = 15;

这次,我们再来输出m.a,发现m.a的值还是10,并没有改变,m对象和n对象是虽然所有的值都是一样的,但是在堆里面,对应的不是同一个了,这个就是深拷贝。

浅拷贝(Shallow Copy)VS深拷贝(Deep Copy)

浅拷贝(Shallow Copy) VS 深拷贝(Deep Copy)

浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。

浅拷贝的实现方式

1、可以通过简单的赋值实现

类似上面的例子,当然,我们也可以封装一个简单的函数,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function simpleClone(initalObj) {    
      var obj = {};    
      for ( var i in initalObj) {
        obj[i] = initalObj[i];
      }    
      return obj;
    }

    var obj = {
      a: "hello",
      b:{
          a: "world",
          b: 21
        },
      c:["Bob", "Tom", "Jenny"],
      d:function() {
          alert("hello world");
        }
    }
    var cloneObj = simpleClone(obj);
    console.log(cloneObj.b);
    console.log(cloneObj.c);
    console.log(cloneObj.d);

    cloneObj.b.a = "changed";
    cloneObj.c = [1, 2, 3];
    cloneObj.d = function() { alert("changed"); };
    console.log(obj.b);
    console.log(obj.c);
    console.log(obj.d);

2、Object.assign()实现

Object.assign()方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是Object.assign()进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。

1
2
3
4
var obj={a:{a:"hello",b:21}};
var initalObj=Object.assign({},obj);
initalObj.a.a="changed";
console.log(obj.a.a);//"changed"

注意:当object只有一层的时候,是深拷贝,例如如下:

1
2
3
4
5
6
7
var obj1 = { a: 10, b: 20, c: 30 };
var obj2 = Object.assign({}, obj1);
obj2.b = 100;
console.log(obj1);
// { a: 10, b: 20, c: 30 } <-- 沒被改到
console.log(obj2);
// { a: 10, b: 100, c: 30 }

深拷贝的实现方式

1、方法一还是手动复制

和上面的举例一样,手动复制可以实现深拷贝。

2、对象只有一层的话可以使用上面的:Object.assign()函数

3、转成JSON再转回来

1
2
3
4
5
6
7
8
9
10
11
var obj1 = { body: { a: 10 } };
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.body.a = 20;
console.log(obj1);
// { body: { a: 10 } } &lt;-- 沒被改到
console.log(obj2);
// { body: { a: 20 } }
console.log(obj1 === obj2);
// false
console.log(obj1.body === obj2.body);
// false

用JSON.stringify把对象转成字符串,再用JSON.parse把字符串转成新的对象。

可以封装如下函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var cloneObj = function(obj){
    var str, newobj = obj.constructor === Array ? [] : {};
    if(typeof obj !== 'object'){
        return;
    } else if(window.JSON){
        str = JSON.stringify(obj), //系列化对象
        newobj = JSON.parse(str); //还原
    } else {
        for(var i in obj){
            newobj[i] = typeof obj[i] === 'object' ?
            cloneObj(obj[i]) : obj[i];
        }
    }
    return newobj;
};

4、递归拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : {};            
      arguments.callee(prop, obj[i]);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}
var str = {};
var obj = { a: {a: "hello", b: 21} };
deepClone(obj, str);
console.log(str.a);

5、使用Object.create()方法

直接使用var newObj=Object.create(oldObj),可以达到深拷贝的效果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function deepClone(initalObj, finalObj) {    
  var obj = finalObj || {};    
  for (var i in initalObj) {        
    var prop = initalObj[i];        // 避免相互引用对象导致死循环,如initalObj.a = initalObj的情况
    if(prop === obj) {            
      continue;
    }        
    if (typeof prop === 'object') {
      obj[i] = (prop.constructor === Array) ? [] : Object.create(prop);
    } else {
      obj[i] = prop;
    }
  }    
  return obj;
}

6、jquery

jquery有提供一个$.extend可以用来做Deep Copy。

1
2
3
4
5
6
7
8
9
var $ = require('jquery');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = $.extend(true, {}, obj1);
console.log(obj1.b.f === obj2.b.f);
// false

7、lodash

另外一个很热门的函数库lodash,也有提供_.cloneDeep用来做Deep Copy。

1
2
3
4
5
6
7
8
9
var _ = require('lodash');
var obj1 = {
    a: 1,
    b: { f: { g: 1 } },
    c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false

这是我比较推荐的方法,性能还不错,使用起来也很简单。

结束语

以上就是今天为大家带来的全部有关深拷贝和浅拷贝的相关知识,如果大家有更好的方法,也欢迎留言。

「如果觉得我的文章对您有用,请帮助本站成长」

赞(13) 打赏

觉得文章有用就打赏一下文章作者

支付宝
微信
13

觉得文章有用就打赏一下文章作者

支付宝
微信
标签:

上一篇:

下一篇:

共有 2 条评论 - 理解js的深拷贝和浅拷贝原理和实现的方法

  1. IDhdu Linux Chrome 62.0.3202.84

    深拷贝浅拷贝一直以来摸不透,今天结合实际例子,更好理解

    1. 码云 Windows 7 Chrome 69.0.3497.100

      @IDhdu谢谢和支持

博客简介

码云笔记: mybj123.com,一个关注Web前端开发技术的博客,主要记录和总结前端工作中常用的知识及我的生活。
更多博客详情请看关于博客

圈子

关注微信公众号
关注微信公众号

精彩评论

服务热线:
 13888888888

 QQ在线交流