[[IT知识]] JS深拷贝与浅拷贝:一文读懂它们的区别与实现方法

[复制链接]
查看: 29|回复: 0
发表于 昨天 15:44 | 显示全部楼层 | 阅读模式
易博V9下载

JS深拷贝与浅拷贝:一文读懂它们的区别与实现方法

前言

这个月太忙了,一直没时间更新博客。帮楼下房友用react写系统,react终于应用于实际项目了。房友前端技术栈是react+dva+antd+nodejs+webpack,这些后面有时间具体总结。今天写一下js的深拷贝和浅拷贝,这个名词经常听到,但是可能有的同学不是很明白他们的意思。今天就用浅显易懂的语言和大家介绍一下!

浅拷贝

浅拷贝其实我之前有文章具体讲过,不过没有提及这个名词罢了,例如:js内存空间及this关键词详解,这篇文章,里面讲到如下:

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

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

深拷贝

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

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

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

深拷贝和浅拷贝

深拷贝和浅拷贝的示意图大致如下:


JS深拷贝与浅拷贝:一文读懂它们的区别与实现方法

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

浅拷贝的实现方式

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

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

  1. function simpleClone(initalObj) {
  2. var obj = {};
  3. for ( var i in initalObj) {
  4. obj[i] = initalObj[i];
  5. }
  6. return obj;
  7. }
  8. var obj = {
  9. a: "hello",
  10. b:{
  11. a: "world",
  12. b: 21
  13. },
  14. c:["Bob", "Tom", "Jenny"],
  15. d:function() {
  16. alert("hello world");
  17. }
  18. }
  19. var cloneObj = simpleClone(obj);
  20. console.log(cloneObj.b);
  21. console.log(cloneObj.c);
  22. console.log(cloneObj.d);
  23. cloneObj.b.a = "changed";
  24. cloneObj.c = [1, 2, 3];
  25. cloneObj.d = function() { alert("changed"); };
  26. console.log(obj.b);
  27. console.log(obj.c);
  28. console.log(obj.d);
复制代码

2、Object.assign()实现

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

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

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

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

深拷贝的实现方式

1、方法一还是手动复制

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

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

3、转成 JSON 再转回来

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

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

可以封装如下函数

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

4、递归拷贝

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

5、使用Object.create()方法

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

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

6、jquery

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

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

7、lodash

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

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

这个性能还不错,使用起来也很简单。

小结

上面就是对js深拷贝和浅拷贝的总结,其中部分参考了一些文章, 例如

JavaScript中的深拷贝和浅拷贝?

关于 JS 中的浅拷贝和深拷贝

JavaScript中对象的深拷贝

易博软件介绍
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1、请认真发帖,禁止回复纯表情,纯数字等无意义的内容!帖子内容不要太简单!
2、提倡文明上网,净化网络环境!抵制低俗不良违法有害信息。
3、如果你对主帖作者的帖子不屑一顾的话,请勿回帖。谢谢合作!
3、问答求助区发帖求助后,如有其他用户热心帮您解决问题后,请自觉点击设为最佳答案按钮。

 
 
QQ在线客服
QQ技术支持
工作时间:
8:00-18:00
软著登字:
1361266号
官方微信扫一扫
weixin

QQ|小黑屋|Archiver|慈众营销 ( 粤ICP备15049986号 )|网站地图

自动发帖软件 | 自动发帖器 | 营销推广软件 | 网络营销工具 | 网络营销软件 | 网站推广工具 | 网络推广软件 | 网络推广工具 | 网页推广软件 | 信息发布软件 | 网站推广工具 | 网页推广软件

Powered by Discuz! X3.4   © 2012-2020 Comsenz Inc.  慈众科技 - Collect from 深圳吉宝泰佛文化有限公司 公司地址:罗湖区黄贝街道深南东路集浩大厦A1403

返回顶部 返回列表