2019独角兽企业重金招聘Python工程师标准>>>

一、数据类型

数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型。

1、基本数据类型的特点:直接存储在栈(stack)中的数据

2、引用数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里

引用数据类型在栈中存储了指针,该指针指向堆中该实体的起始地址。当解释器寻找引用值时,会首先检索其在栈中的地址,取得地址后从堆中获得实体。

堆内存

二、浅拷贝与深拷贝

深拷贝和浅拷贝是只针对Object和Array这样的引用数据类型的

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

示意图

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

三、赋值和浅拷贝的区别

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

浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。即默认拷贝构造函数只是对对象进行浅拷贝复制(逐个成员依次拷贝),即只复制对象空间而不复制资源。

我们先来看两个例子,对比赋值与浅拷贝会对原对象带来哪些改变?

// 对象赋值
var obj1 = {'name' : 'zhangsan','age' :  '18','language' : [1,[2,3],[4,5]],
};
var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二","三"];
console.log('obj1',obj1)
console.log('obj2',obj2)

对象赋值

// 浅拷贝
var obj1 = {'name' : 'zhangsan','age' :  '18','language' : [1,[2,3],[4,5]],
};
var obj3 = shallowCopy(obj1);
obj3.name = "lisi";
obj3.language[1] = ["二","三"];
function shallowCopy(src) {var dst = {};for (var prop in src) {if (src.hasOwnProperty(prop)) {dst[prop] = src[prop];}}return dst;
}
console.log('obj1',obj1)
console.log('obj3',obj3)

浅拷贝

上面例子中,obj1是原始数据,obj2是赋值操作得到,而obj3浅拷贝得到。我们可以很清晰看到对原始数据的影响,具体请看下表:

对原始数据的影响

四、浅拷贝的实现方式

1. Object.assign()

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

var obj = { a: {a: "kobe", b: 39} };
var initalObj = Object.assign({}, obj);
initalObj.a.a = "wade";
console.log(obj.a.a); // wade

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

let obj = {username: 'kobe'
};
let obj2 = Object.assign({},obj);
obj2.username = 'wade';
console.log(obj);//{username: "kobe"}

2. Array.prototype.concat()

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

修改新对象会改到原对象:

修改新对象会改到原对象

3. Array.prototype.slice()

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

同样修改新对象会改到原对象:

同样修改新对象会改到原对象

关于Array的slice和concat方法的补充说明:Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。

原数组的元素会按照下述规则拷贝:

  1. 如果该元素是个对象引用(不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。

  2. 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。

可能这段话晦涩难懂,我们举个例子,将上面的例子小作修改:

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

小作修改

五、深拷贝的实现方式

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)

JSON.parse

原理: 用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。

这种方法虽然可以实现数组或对象深拷贝,但不能处理函数。

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

不能处理函数

这是因为 JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,不能接受函数。

2. 手写递归方法

递归方法实现深度克隆原理:遍历对象、数组直到里边都是基本数据类型,然后再去复制,就是深度拷贝。

   // 定义检测数据类型的功能函数function checkedType(target) {return Object.prototype.toString.call(target).slice(8, -1)}// 实现深度克隆---对象/数组function clone(target) {// 判断拷贝的数据类型// 初始化变量result 成为最终克隆的数据let result, targetType = checkedType(target)if (targetType === 'object') {result = {}} else if (targetType === 'Array') {result = []} else {return target}// 遍历目标数据for (let i in target) {// 获取遍历数据结构的每一项值。let value = target[i]// 判断目标结构里的每一值是否存在对象/数组if (checkedType(value) === 'Object' ||checkedType(value) === 'Array') { //对象/数组里嵌套了对象/数组// 继续遍历获取到value值result[i] = clone(value)} else { // 获取到value值是基本的数据类型或者是函数。result[i] = value;}}return result}// 定义检测数据类型的功能函数function checkedType(target) {return Object.prototype.toString.call(target).slice(8, -1)}// 实现深度克隆---对象/数组function clone(target) {// 判断拷贝的数据类型// 初始化变量result 成为最终克隆的数据let result, targetType = checkedType(target)if (targetType === 'object') {result = {}} else if (targetType === 'Array') {result = []} else {return target}// 遍历目标数据for (let i in target) {// 获取遍历数据结构的每一项值。let value = target[i]// 判断目标结构里的每一值是否存在对象/数组if (checkedType(value) === 'Object' ||checkedType(value) === 'Array') { // 对象/数组里嵌套了对象/数组// 继续遍历获取到value值result[i] = clone(value)} else { // 获取到value值是基本的数据类型或者是函数。result[i] = value;}}return result}

3. 函数库lodash

该函数库也有提供 _.cloneDeep 用来做 Deep Copy。

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

觉得不错请点赞支持,欢迎留言或进我的个人群855801563领取【架构资料专题目合集90期】、【BATJTMD大厂JAVA面试真题1000+】,本群专用于学习交流技术、分享面试机会,拒绝广告,我也会在群内不定期答题、探讨。

转载于:https://my.oschina.net/u/3959491/blog/3045743

彻底讲明白浅拷贝与深拷贝相关推荐

  1. 【彻底讲明白浅拷贝与深拷贝】

    一.数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型. 1.基本数据类型的特点:直接存储在栈(stack) ...

  2. 理解浅拷贝和深拷贝以及实现方法

    一.数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和引用数据类型Object,包含(function,Array,Da ...

  3. 说说JS中的浅拷贝与深拷贝

    outline: 为什么要说JS中深拷贝与浅拷贝 JS对类型的分类 immutable与mutable 简单类型检测 浅拷贝VS深拷贝 为什么要说JS中深拷贝与浅拷贝 近来在研读underscore的 ...

  4. js之浅拷贝和深拷贝

    js数据类型主要分基本数据类型和引用数据类型.前者包括Number,String等,后者主要是Object,因此以下会针对不同的数据类型来分析,需要的朋友可以参考一下 1.js内存 js内存,或者说大 ...

  5. Python 赋值、浅拷贝、深拷贝的区别?

    http://songlee24.github.io/2014/08/15/python-FAQ-02/ 在写Python过程中,经常会遇到对象的拷贝,如果不理解浅拷贝和深拷贝的概念,你的代码就可能出 ...

  6. python函数方法里面用浅复制深复制_图解 Python 浅拷贝与深拷贝

    Python 中的赋值语句不会创建对象的拷贝,仅仅只是将名称绑定至一个对象.对于不可变对象,通常没什么差别,但是处理可变对象或可变对象的集合时,你可能需要创建这些对象的 "真实拷贝" ...

  7. C#浅拷贝与深拷贝区别

    也许会有人这样解释C# 中浅拷贝与深拷贝区别: 浅拷贝是对引用类型拷贝地址,对值类型直接进行拷贝. 不能说它完全错误,但至少还不够严谨.比如:string 类型咋说? 其实,我们可以通过实践来寻找答案 ...

  8. 【Python成长之路】快速理解复制、浅拷贝、深拷贝

    哈喽大家好,我是鹏哥. 今天想聊聊的主题是 -- 浅拷贝/深拷贝. ~~~上课铃~~~ 赤伶HITA - 赤伶 1 写在前面 浅拷贝.深拷贝的知识点,在python笔试和面试的过程中,经常被作为考题来 ...

  9. 引用拷贝、对象拷贝、浅拷贝、深拷贝 到底是什么【详细例子介绍】

    首先要知道的: Java的数据类型分为基本数据类型和引用数据类型. 拷贝一个对象,可以使用 Cloneable接口的clone()方法. 对象拷贝分为 浅拷贝 和 深拷贝,这两种拷贝都是从引用拷贝 引 ...

  10. 彻底理解Python中浅拷贝和深拷贝的区别

    目录 前言 1. 浅拷贝和深拷贝的概念 2. is和==的区别 3. 赋值操作 4. copy模块里面的copy()方法 5. copy模块里面的deepcopy()方法 6.字典自带的copy方法 ...

最新文章

  1. 特斯拉首次达成连续4季度盈利:车卖的少了,钱却挣得多了
  2. Android学习系列(10)--App列表之拖拽ListView(上)
  3. 企业研发人员配备比例_企业管理人员合理配置比例
  4. 前端学习(1263):post方式的参数传递
  5. python调用百度地图画轨迹图_[宜配屋]听图阁
  6. dz论坛连接mysql数据库_dz论坛搬家后连接数据库等教程
  7. Linux文件和目录的属性及权限
  8. 数据结构笔记(十九)-- 二叉树性质
  9. STM32F0xx_TIM基本延时配置详细过程
  10. pycharm安装scrapy失败_大数据开发神器——Scrapy 框架(读懂Spider流程图)
  11. 视频教程-SAP S4 HANA财务模块入门到精通-ERP
  12. 中国麻将:世界上最早的区块链项目
  13. 【安装windows10 RTX3090 tensorflow的开发环境】
  14. 蒸汽凝结水颜色发红十种常见除铁处理技术优劣对比
  15. matlab编写数学公式计算,关于MATLAB Function实现数学运算的相关介绍
  16. Robocup2D入门笔记(1)——概述
  17. 亿信华辰:怎样去断定一份数据的质量高低?数据质量如何评估?
  18. 数据可视化 数据可视化看板项目一:(1)模拟实时数据 -使用MYSQL的事件建立动态模拟数据,每秒更新一次 (1)
  19. 淘宝客 WebView打开淘宝链接失败的解决方法
  20. MarkDown API

热门文章

  1. MongoDB(一)——简介
  2. 烂泥:KVM虚拟机的关机与开启
  3. lesmars实验室两个有关点云的专利
  4. Linux DRM 理解
  5. 文件夹里没有index.html,gatsby-cli建立后没有index.html文件吗?
  6. mysql怎么判断多行数据日期是否连续_MySQL学习笔记(一)
  7. Linux线程管理必备:互斥量与条件变量
  8. 内核中的中断函数request_irq()
  9. T - hiho字符串 HihoCoder - 1485 (..map的使用把)
  10. 云服务器怎么增加d盘_怎么租用美国云服务器比较便宜?