在 JS 中有一些基本类型像是NumberStringBoolean,而对象就是像这样的东西{ 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

虽然obj1obj2是不同对象,但他们会共享同一个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的浅拷贝和深拷贝相关推荐

  1. javascript中浅拷贝和深拷贝的理解

    javascript中浅拷贝和深拷贝的理解 什么是拷贝? 简单地说就是复制,对数据的复制 浅拷贝:改变拷贝者的值,被拷贝者的值也会变化 深拷贝:改变拷贝者的值,被拷贝者的值不会变化 由于基本数据类型是 ...

  2. JavaScript 的浅拷贝与深拷贝,栈与堆,深拷贝的多种方法,最全讲解

    栈与堆 在 JavaScript 中也会将基本类型与对象类型称为值类型与引用类型.这个是因为基本类型中存储的是数据完整的值,而在引用类型中存储的仅是指向数据的一个地址. 在大部分编程语言中,变量会被存 ...

  3. javascript的浅拷贝与深拷贝

    一.数据类型 JavaScript有lia两种数据类型: 1.基本数据类型:Number.String.Undefined.Null.Boolean.Symbol.这六种数据类型数据段简单,大小可以确 ...

  4. JavaScript 面向对象编程(三) —— 函数进阶 / 严格模式 / 高阶函数 / 闭包 / 浅拷贝和深拷贝

    本篇为 JavaScript 进阶 ES6 系列笔记第三篇,将陆续更新后续内容.参考:JavaScript 进阶面向对象 ES6 :ECMAScript 6 入门 系列笔记: JavaScript 面 ...

  5. JavaScript学习(七十八)—实现对数据的浅拷贝和深拷贝

    JavaScript学习(七十八)-实现对数据的浅拷贝和深拷贝 一.浅拷贝 浅拷贝:对于引用类型的数据只拷贝该数据的地址,这种拷贝称为浅拷贝 注意:拷贝出来的数据和原有的数据指向同一个空间,即他们操作 ...

  6. JavaScript学习(七十五)—图解浅拷贝和深拷贝

    JavaScript学习(七十五)-图解浅拷贝和深拷贝 浅拷贝 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用 Object.assign(target, ...sources);ES6新增方法可以 ...

  7. JavaScript知识之 浅拷贝与深拷贝

    值传递与引用传递 JavaScript中有两种数据类型:基本数据类型和引用数据类型两种. 从名字上来看,大概也能猜到它们的区别: 基本数据类型 值直接存储在栈内存中 对于引用类型来说 它存储了一个引用 ...

  8. 43 JavaScript中的浅拷贝与深拷贝

    技术交流QQ群:1027579432,欢迎你的加入! 欢迎关注我的微信公众号:CurryCoder的程序人生 1.浅拷贝与深拷贝 浅拷贝只是拷贝一层,更深层次对象级别的只拷贝引用. 深拷贝拷贝多层,每 ...

  9. js 浅拷贝直接赋值_浅析JavaScript解析赋值、浅拷贝和深拷贝的区别

    一.赋值(Copy) 赋值是将某一数值或对象赋给某个变量的过程,分为: 1.基本数据类型:赋值,赋值之后两个变量互不影响 2.引用数据类型:赋**址**,两个变量具有相同的引用,指向同一个对象,相互之 ...

最新文章

  1. mongodb在mysql中怎么用,mongoDB数据库基本操作
  2. html前端获取地理位置_简单阐述什么是前端?后端?
  3. 十四.200创业课程获得百万--不良,不要启动
  4. mysql select db 废弃_php 项目放服务器显示mysql_connect 已经废弃 ?
  5. 这家工作室花三年把塞尔达做进RogueLike里面
  6. mongodb 输出数组字段_MongoDb文档操作、索引操作
  7. avr flash_AVR | USART家庭自动化
  8. mybatis3 配置文件解析
  9. C#文件目录IO常见操作汇总
  10. PyTorch报错“/.../Loss.cu: ... [59,0,0] Assertion input_val >= zero input_val <= one failed.”
  11. ELK笔记(一)elasticsearch安装
  12. 多重搜索算法_Android多重搜寻,例如传送,搜寻联络人
  13. P3089 [USACO13NOV]POGO的牛Pogo-Cow
  14. 秒杀系统的核心点都在这里,快来取
  15. 预防 Android Dex 64k Method Size Limit
  16. OSGB数据的纹理压缩
  17. pyqt4的一些相关资料整理
  18. matlab 角速度,从您的移动设备获取并绘制角速度和方向数据
  19. 跳跃表skiplist简析
  20. 软件测试验收测试的重点是什么,实施软件验收测试的常用策略

热门文章

  1. 功能测试包含哪些测试_一小时复习,期末考试必过 重邮软件测试题总结
  2. java getscale_Java MajorType.getScale方法代碼示例
  3. python下载邮箱附件_基于Python3 下载邮箱附件,并解压到指定文件夹
  4. Java布局怎么加图片组件_java – 将图像缩略图添加到网格中的布局...
  5. python常用代码_Python常用算法学习(4) 数据结构(原理+代码)-最全总结
  6. php ci session获取值,CI3.1 Session类取不到值的问题
  7. pdfbox 按章节读取_2020年智慧树APP微生物与健康第五单元章节测试网课答案大学课后答案...
  8. 英雄会被表彰,这些技术与代码也将被历史铭记
  9. 为什么PCB板通常是绿色的?
  10. 关于使用两个GTP/GTX出现[DRC RTSTAT-1]error([route 35-54] critical warning)的问题详解