浅拷贝


创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝


将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象

快速理解

  • 浅拷贝赋值引用地址,修改值会影响原始值
  • 深拷贝引用类型会重新创建一个对象,不会影响初始值

赋值 vs 浅拷贝 vs 深拷贝

这三者的区别如下,不过比较的前提都是针对引用类型

  • 当我们把一个对象赋值给一个新的变量时,赋的其实是该对象的在栈中的地址,而不是堆中的数据。也就是两个对象指向的是同一个存储空间,无论哪个对象发生改变,其实都是改变的存储空间的内容,因此,两个对象是联动的。

  • 浅拷贝:重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响

  • 深拷贝:从堆内存中开辟一个新的区域存放新对象,对对象中的子对象进行递归拷贝,拷贝前后的两个对象互不影响

我们先来看下面的例子,对比赋值与深/浅拷贝得到的对象修改后对原始对象的影响:

// 对象赋值
let obj1 = {name : '浪里行舟',arr : [1,[2,3],4],
};
let obj2 = obj1;
obj2.name = "阿浪";
obj2.arr[1] =[5,6,7] ;
console.log('obj1',obj1) // obj1 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }
console.log('obj2',obj2) // obj2 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }
// 浅拷贝
let obj1 = {name : '浪里行舟',arr : [1,[2,3],4],
};
let obj3=shallowClone(obj1)
obj3.name = "阿浪";
obj3.arr[1] = [5,6,7] ; // 新旧对象还是共享同一块内存
// 这是个浅拷贝的方法
function shallowClone(source) {var target = {};for(var i in source) {if (source.hasOwnProperty(i)) {target[i] = source[i];}}return target;
}
console.log('obj1',obj1) // obj1 { name: '浪里行舟', arr: [ 1, [ 5, 6, 7 ], 4 ] }
console.log('obj3',obj3) // obj3 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }
// 深拷贝
let obj1 = {name : '浪里行舟',arr : [1,[2,3],4],
};
let obj4=deepClone(obj1)
obj4.name = "阿浪";
obj4.arr[1] = [5,6,7] ; // 新对象跟原对象不共享内存
// 这是个深拷贝的方法
function deepClone(obj) {if (obj === null) return obj; if (obj instanceof Date) return new Date(obj);if (obj instanceof RegExp) return new RegExp(obj);if (typeof obj !== "object") return obj;let cloneObj = new obj.constructor();for (let key in obj) {if (obj.hasOwnProperty(key)) {// 实现一个递归拷贝cloneObj[key] = deepClone(obj[key]);}}return cloneObj;
}
console.log('obj1',obj1) // obj1 { name: '浪里行舟', arr: [ 1, [ 2, 3 ], 4 ] }
console.log('obj4',obj4) // obj4 { name: '阿浪', arr: [ 1, [ 5, 6, 7 ], 4 ] }

浅拷贝的实现方式

1.Object.assign()

Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。

let obj1 = { person: {name: "kobe", age: 41},sports:'basketball' };
let obj2 = Object.assign({}, obj1);
obj2.person.name = "wade";
obj2.sports = 'football'
console.log(obj1); // { person: { name: 'wade', age: 41 }, sports: 'basketball' }

2.lodash的_.clone方法

var _ = require('lodash');
var obj1 = {a: 1,b: { f: { g: 1 } },c: [1, 2, 3]
};
var obj2 = _.clone(obj1);
console.log(obj1.b.f === obj2.b.f);// true

3.扩展运算符…

let obj1 = { name: 'Kobe', address:{x:100,y:100}}
let obj2= {... obj1}
obj1.address.x = 200;
obj1.name = 'wade'
console.log('obj2',obj2) // obj2 { name: 'Kobe', address: { x: 200, y: 100 } }

4.Array.prototype.concat()

let arr = [1, 3, {username: 'kobe'}];
let arr2 = arr.concat();
arr2[2].username = 'wade';
console.log(arr); //[ 1, 3, { username: 'wade' } ]

5.Array.prototype.slice()

let arr = [1, 3, {username: ' kobe'}];
let arr3 = arr.slice();
arr3[2].username = 'wade'
console.log(arr); // [ 1, 3, { username: 'wade' } ]。

深拷贝的实现方式

1.JSON.parse(JSON.stringify())

let arr = [1, 3, {username: ' kobe'
}];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'duncan';
console.log(arr, arr4)

注:存在问题,不支持函数、Date、正则等数据,不支持引用

2.lodash的_.cloneDeep方法

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

3.手写递归方法

    const deepClone = (a, cache) => {if (!cache) {cache = new Map()        }if (a instanceof Object) {  if (cache.get(a)) {return cache.get(a) // 如果已拷贝过,则直接返回}   let resultif (a instanceof Function) {    // 如果是函数if (a.prototype) {  // 普通函数result = function () {return a.apply(this, arguments)}} else {    // 箭头函数result = (...args) => {return a.call(undefined, ...args)}}} else if (a instanceof Array) {    // 如果是数组result = []} else if (a instanceof Date) {     // 如果是日期result = new Date(a - 0)} else if (a instanceof RegExp) {     // 如果是正则result = new RegExp(a.source, a.flags)} else {    // 其他引用类型result = {}}cache.set(a, result)    // 防止循环引用for (let key in a) {if (a.hasOwnProperty(key)) {    // 只拷贝自身身上的属性result[key] = deepClone(a[key], cache)}}return result} else {    return a}}

参考文档

如何写出一个惊艳面试官的深拷贝?

浅拷贝与深拷贝

详解浅拷贝,深拷贝及实现方法相关推荐

  1. 一分钟详解initUndistortRectifyMap函数bug修复方法

    本文首发于微信公众号「3D视觉工坊」--一分钟详解initUndistortRectifyMap函数bug修复方法 在上一篇文章OpenCV中initUndistortRectifyMap函数存在bu ...

  2. Fragment详解之二——基本使用方法

    前言:依然没有前言--文章写的太快,生活过得太有章程,前言都不知道写什么了-- 相关文章: 1.<Fragment详解之一--概述> 2.<Fragment详解之二--基本使用方法& ...

  3. java time 周期性执行,详解ScheduledExecutorService的周期性执行方法

    详解 ScheduledExecutorService 的周期性执行方法 在最近的工作中,需要实现一个当一个任务执行完后,再等 100 毫秒然后再次执行的功能.当时最先反映到的就是 java 线程池的 ...

  4. 图文详解Java环境变量配置方法

    今天动力节点java学院小编为大家介绍"图文详解Java环境变量配置方法",希望对各位小伙伴有帮助,下面就和小编一起来看看Java环境变量配置方法吧. 首先是要安装JDK,JDK安 ...

  5. java mysbatis select_java相关:详解Mybatis中的select方法

    java相关:详解Mybatis中的select方法 发布于 2020-7-3| 复制链接 摘记: selectById方法根据id,查询记录 ```java public void updateRe ...

  6. 详解ScheduledExecutorService的周期性执行方法

    2019独角兽企业重金招聘Python工程师标准>>> 详解 ScheduledExecutorService 的周期性执行方法 在最近的工作中,需要实现一个当一个任务执行完后,再等 ...

  7. jQuery选择器代码详解(一)——Sizzle方法

    对jQuery的Sizzle各方法做了深入分析(同时也参考了一些网上资料)后,将结果分享给大家.我将采用连载的方式,对Sizzle使用的一些方法详细解释一下,每篇文章介绍一个方法. 若需要转载,请写明 ...

  8. python列转行函数_Python pandas 列转行操作详解(类似hive中explode方法)

    最近在工作上用到Python的pandas库来处理excel文件,遇到列转行的问题.找了一番资料后成功了,记录一下. 1. 如果需要爆炸的只有一列: df=pd.DataFrame({'A':[1,2 ...

  9. linux parted分区教程,分区工具parted的详解及常用分区使用方法

    分区工具parted的详解及常用分区使用方法 一. parted的用途及说明 概括使用说明: parted用于对磁盘(或RAID磁盘)进行分区及管理,与fdisk分区工具相比,支持2TB以上的磁盘分区 ...

  10. 图文详解placeholder属性的使用方法以及如何修改placeholder的默认样式

    在页面布局时经常会用到input输入框,有时为了提示用户输入正确的信息,需要用placeholder属性加以说明.这篇文章就和大家讲讲placeholder属性怎么用以及如何修改placeholder ...

最新文章

  1. RDKit | 基于RDKit绘制黑白颜色的分子
  2. Java锁优化思路及JVM实现
  3. myeclipse10.7激活
  4. 成大事必备9种能力、9种手段、9种心态
  5. WORD页边距、行距、页码、页眉页脚
  6. windows软链接的建立及删除
  7. Leedcode4-sort listnode 归并排序
  8. 处理table 超出部分滚动问题
  9. AWT_addMouseListener鼠标监听事件(Java)
  10. 2021-08-17 String to Integet atoi, ratate list
  11. Mask R-CNN算法详解
  12. sfm点云代码_三维重建的方法SFM
  13. python while循环的用法_Python while循环语句详解
  14. 神剑轩辕java,上古十大神剑有哪些 有一把居然是专门用来弑君杀父的
  15. Unity 3D光源-Directional平行光/逆光效果,光晕详解、教程
  16. 闪讯利用openwrt路由器拨号教程(五)
  17. Dapr+Net6 服务调用09:集群指标收集-普罗米修斯
  18. 日常面试刷题9-29
  19. 【加密与解密】C#如何读取pem的KEY文件
  20. VVC中的熵编码-JVET提案Q2002

热门文章

  1. python后退快捷键_IntelliJ IDEA的后退快捷键是什么?
  2. 使用qemu模拟X86处理器加载linux kernel+busybox文件系统并调试
  3. 现行高考政策公平 辩论_辩论文:现行高考模式有(不)利于选拔入才
  4. 红帽 Linux Redhat6.4安装MySQL 5.1
  5. 【CentOS-7.4】Sphinx 安装与简单配置
  6. 什么是糊涂窗口综合症
  7. <C++>多继承以及典型的菱形继承案例
  8. Tableau常用可视化图形介绍及其适用场景
  9. 文科生 python 简书_文科生学 Python 系列 15:泰坦尼克数据 1
  10. 计算机右侧不显示桌面,显示器右边显示不出来怎么办