关于JavaScript的浅拷贝和深拷贝
在 JS 中有一些基本类型像是Number
、String
、Boolean,
而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他们的传值方式。
基本类型是按值传递,像是这样:在修改a
时并不会改到b
var a = 25; var b = a; b = 18; console.log(a);//25 console.log(b);//18
但对象就不同,对象传的是按引用传值:
var obj1 = { a: 10, b: 20, c: 30 }; var obj2 = obj1; obj2.b = 100; console.log(obj1); // { a: 10, b: 100, c: 30 } <-- b 被改到了 console.log(obj2); // { a: 10, b: 100, c: 30 }
复制一份obj1
叫做obj2,
然后把obj2.b
改成100,
但却不小心改到obj1.b,
因为他们根本是同一个对象,这就是所谓的浅拷贝。
要避免这样的错误发生就要写成这样:
var obj1 = { a: 10, b: 20, c: 30 }; var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c }; obj2.b = 100; console.log(obj1); // { a: 10, b: 20, c: 30 } <-- b 沒被改到 console.log(obj2); // { a: 10, b: 100, c: 30 }
这样就是深拷贝,不会改到原本的obj1。
浅拷贝(Shallow Copy) VS 深拷贝(Deep Copy)
浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改到原对象。
浅拷贝的实现方式
也就是简单地复制而已
1、简单地复制语句
<script type="text/javascript">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);</script>
结果为:
2、Object.assign()
方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。但是 Object.assign
是ES6的新函数。Object.assign()Object.assign()
进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身。
Object.assign(target, ...sources)
参数:
target:目标对象。
sources:任意多个源对象。
返回值:目标对象会被返回。
var obj = { a: {a: "hello", b: 21} }; var initalObj = Object.assign({}, obj);initalObj.a.a = "changed"; console.log(obj.a.a); // "changed"
兼容性:
需要注意的是:
Object.assign()可以处理一层的深度拷贝,如下:
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 }
深拷贝的实现方式
要完全复制又不能修改到原对象,这时候就要用 Deep Copy,这里会介绍几种Deep Copy 的方式。
1、手动复制
把一个对象的属性复制给另一个对象的属性
var obj1 = { a: 10, b: 20, c: 30 }; var obj2 = { a: obj1.a, b: obj1.b, c: obj1.c }; obj2.b = 100; console.log(obj1); // { a: 10, b: 20, c: 30 } <-- 沒被改到 console.log(obj2); // { a: 10, b: 100, c: 30 }
但这样很麻烦,要一个一个自己复制;而且这样的本质也不能算是 Deep Copy,因为对象里面也可能回事对象,如像下面这个状况:
var obj1 = { body: { a: 10 } }; var obj2 = { body: obj1.body }; obj2.body.a = 20; console.log(obj1); // { body: { a: 20 } } <-- 被改到了 console.log(obj2); // { body: { a: 20 } } console.log(obj1 === obj2); // false console.log(obj1.body === obj2.body); // true
虽然obj1
跟obj2
是不同对象,但他们会共享同一个obj1.body
,
所以修改obj2.body.a
时也会修改到旧的。
2、对象只有一层的话可以使用上面的:Object.assign()函数
Object.assign({}, obj1)
的意思是先建立一个空对象{},接着把obj1
中所有的属性复制过去,所以obj2
会长得跟obj1
一样,这时候再修改obj2.b
也不会影响obj1。
因为Object.assign
跟我们手动复制的效果相同,所以一样只能处理深度只有一层的对象,没办法做到真正的 Deep Copy。不过如果要复制的对象只有一层的话可以考虑使用它。
3、转成 JSON 再转回来
用JSON.stringify
把对象转成字符串,再用JSON.parse
把字符串转成新的对象。
var obj1 = { body: { a: 10 } }; var obj2 = JSON.parse(JSON.stringify(obj1)); obj2.body.a = 20; console.log(obj1); // { body: { a: 10 } } <-- 沒被改到 console.log(obj2); // { body: { a: 20 } } console.log(obj1 === obj2); // false console.log(obj1.body === obj2.body); // false
这样做是真正的Deep Copy,这种方法简单易用。
但是这种方法也有不少坏处,譬如它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。
这种方法能正确处理的对象只有 Number, String, Boolean, Array, 扁平对象
,即那些能够被 json 直接表示的数据结构。RegExp对象是无法通过这种方式深拷贝。
也就是说,只有可以转成JSON
格式的对象才可以这样用,像function
没办法转成JSON。
var obj1 = { fun: function(){ console.log(123) } }; var obj2 = JSON.parse(JSON.stringify(obj1)); console.log(typeof obj1.fun); // 'function' console.log(typeof obj2.fun); // 'undefined' <-- 没复制
要复制的function
会直接消失,所以这个方法只能用在单纯只有数据的对象。
4、递归拷贝
function deepClone(initalObj, finalObj) { var obj = finalObj || {}; for (var i in initalObj) { if (typeof initalObj[i] === 'object') {obj[i] = (initalObj[i].constructor === Array) ? [] : {}; arguments.callee(initalObj[i], obj[i]);} else {obj[i] = initalObj[i];}} return obj; } var str = {}; var obj = { a: {a: "hello", b: 21} }; deepClone(obj, str); console.log(str.a);
上述代码确实可以实现深拷贝。但是当遇到两个互相引用的对象,会出现死循环的情况。
为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环。
改进版代码如下:
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),可以达到深拷贝的效果。
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。
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。
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
这个性能还不错,使用起来也很简单。
参考:
JavaScript 中对象的深拷贝
关于 JS 中的浅拷贝和深拷贝
转载于:https://www.cnblogs.com/Chen-XiaoJun/p/6217373.html
关于JavaScript的浅拷贝和深拷贝相关推荐
- javascript中浅拷贝和深拷贝的理解
javascript中浅拷贝和深拷贝的理解 什么是拷贝? 简单地说就是复制,对数据的复制 浅拷贝:改变拷贝者的值,被拷贝者的值也会变化 深拷贝:改变拷贝者的值,被拷贝者的值不会变化 由于基本数据类型是 ...
- JavaScript 的浅拷贝与深拷贝,栈与堆,深拷贝的多种方法,最全讲解
栈与堆 在 JavaScript 中也会将基本类型与对象类型称为值类型与引用类型.这个是因为基本类型中存储的是数据完整的值,而在引用类型中存储的仅是指向数据的一个地址. 在大部分编程语言中,变量会被存 ...
- javascript的浅拷贝与深拷贝
一.数据类型 JavaScript有lia两种数据类型: 1.基本数据类型:Number.String.Undefined.Null.Boolean.Symbol.这六种数据类型数据段简单,大小可以确 ...
- JavaScript 面向对象编程(三) —— 函数进阶 / 严格模式 / 高阶函数 / 闭包 / 浅拷贝和深拷贝
本篇为 JavaScript 进阶 ES6 系列笔记第三篇,将陆续更新后续内容.参考:JavaScript 进阶面向对象 ES6 :ECMAScript 6 入门 系列笔记: JavaScript 面 ...
- JavaScript学习(七十八)—实现对数据的浅拷贝和深拷贝
JavaScript学习(七十八)-实现对数据的浅拷贝和深拷贝 一.浅拷贝 浅拷贝:对于引用类型的数据只拷贝该数据的地址,这种拷贝称为浅拷贝 注意:拷贝出来的数据和原有的数据指向同一个空间,即他们操作 ...
- JavaScript学习(七十五)—图解浅拷贝和深拷贝
JavaScript学习(七十五)-图解浅拷贝和深拷贝 浅拷贝 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用 Object.assign(target, ...sources);ES6新增方法可以 ...
- JavaScript知识之 浅拷贝与深拷贝
值传递与引用传递 JavaScript中有两种数据类型:基本数据类型和引用数据类型两种. 从名字上来看,大概也能猜到它们的区别: 基本数据类型 值直接存储在栈内存中 对于引用类型来说 它存储了一个引用 ...
- 43 JavaScript中的浅拷贝与深拷贝
技术交流QQ群:1027579432,欢迎你的加入! 欢迎关注我的微信公众号:CurryCoder的程序人生 1.浅拷贝与深拷贝 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用. 深拷贝拷贝多层,每 ...
- js 浅拷贝直接赋值_浅析JavaScript解析赋值、浅拷贝和深拷贝的区别
一.赋值(Copy) 赋值是将某一数值或对象赋给某个变量的过程,分为: 1.基本数据类型:赋值,赋值之后两个变量互不影响 2.引用数据类型:赋**址**,两个变量具有相同的引用,指向同一个对象,相互之 ...
最新文章
- mongodb在mysql中怎么用,mongoDB数据库基本操作
- html前端获取地理位置_简单阐述什么是前端?后端?
- 十四.200创业课程获得百万--不良,不要启动
- mysql select db 废弃_php 项目放服务器显示mysql_connect 已经废弃 ?
- 这家工作室花三年把塞尔达做进RogueLike里面
- mongodb 输出数组字段_MongoDb文档操作、索引操作
- avr flash_AVR | USART家庭自动化
- mybatis3 配置文件解析
- C#文件目录IO常见操作汇总
- PyTorch报错“/.../Loss.cu: ... [59,0,0] Assertion input_val >= zero input_val <= one failed.”
- ELK笔记(一)elasticsearch安装
- 多重搜索算法_Android多重搜寻,例如传送,搜寻联络人
- P3089 [USACO13NOV]POGO的牛Pogo-Cow
- 秒杀系统的核心点都在这里,快来取
- 预防 Android Dex 64k Method Size Limit
- OSGB数据的纹理压缩
- pyqt4的一些相关资料整理
- matlab 角速度,从您的移动设备获取并绘制角速度和方向数据
- 跳跃表skiplist简析
- 软件测试验收测试的重点是什么,实施软件验收测试的常用策略
热门文章
- 功能测试包含哪些测试_一小时复习,期末考试必过 重邮软件测试题总结
- java getscale_Java MajorType.getScale方法代碼示例
- python下载邮箱附件_基于Python3 下载邮箱附件,并解压到指定文件夹
- Java布局怎么加图片组件_java – 将图像缩略图添加到网格中的布局...
- python常用代码_Python常用算法学习(4) 数据结构(原理+代码)-最全总结
- php ci session获取值,CI3.1 Session类取不到值的问题
- pdfbox 按章节读取_2020年智慧树APP微生物与健康第五单元章节测试网课答案大学课后答案...
- 英雄会被表彰,这些技术与代码也将被历史铭记
- 为什么PCB板通常是绿色的?
- 关于使用两个GTP/GTX出现[DRC RTSTAT-1]error([route 35-54] critical warning)的问题详解