查看原文
其他

[Javascript] 关于 JS 中的浅拷贝和深拷贝

2016-09-24 Larry Lu 前端圈

台湾朋友盧承億投稿


因为前几天在写项目的时候要用到深拷贝,所以就去查了一些相关的数据,趁这个机会做点笔记。

基本类型(Primitive Type) VS 对象(Object)


JS 中有一些基本类型像是NumberStringBoolean,而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他们的传值方式。


基本类型是传 value,像是这样:

var a = 10;
var b = a; b = 20;
console.log(a);//10
console.log(b);//20

在修改a时并不会改到b


但对象就不同,对象传的是reference:

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)


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


如何做到 Deep Copy

要完全复制又不能修改到原对象,这时候就要用 Deep Copy,这里会介绍几种 Deep Copy 的方式


自己手动复制

像上面的范例obj1的属性一个个复制到obj2中:

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时也会修改到旧的。

Object.assign

Object.assign是 ES6 的新函数,可以帮助我们达成跟上面一样的功能。

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 }

Object.assign({}, obj1)的意思是先建立一个空对象{},接着把obj1中所有的属性复制过去,所以obj2会长得跟obj1一样,这时候再修改obj2.b也不会影响obj1。

因为Object.assign跟我们手动复制的效果相同,所以一样只能处理深度只有一层的对象,没办法做到真正的 Deep Copy。不过如果要复制的对象只有一层的话可以考虑使用它。


转成 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,但只有可以转成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会直接消失,所以这个方法只能用在单纯只有数据的对象

jquery

相信大家应该都很熟悉jquery 这个 libraryjquery 有提供一个$.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

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

这是我比较推荐的方法,性能还不错,使用起来也很简单。


如果大家有更好的方法,也欢迎留言。



【React启蒙系列文章】

一、[React启蒙系列] 初探React

二、[React启蒙系列] 学习React前需要理解的名词

三、[React启蒙系列] React和Babel的基本使用

四、[React启蒙系列]React节点

五、[React启蒙系列]使用JSX

六、[React启蒙系列]React 组件属性

七、[React启蒙系列]React 组件状态


【您可能感兴趣的文章】

一、手把手教你用react

二、React入门及资源指引

三、利用ESLint检查代码质量

四、构建一个安全的 JavaScript 沙箱

五、入门Webpack,看这篇就够了

六、第三届CSS大会广州找场地啦~~求介绍~~

七、Web Components 是个什么样的东西

八、JavaScript 被忽视的细节

九、[心得] 如何提高 React App 的性能

十、[心得] 自己动手打造 ES7 开发环境

十一、[译文] 如何运用新技术提升网页速度和性能

十二、你应该知道的HTTP基础知识

十三、Webpack from First Principles

十四、没时间阅读?佐克伯、比尔盖兹、马斯克教你「5小时原则」

十五、没快速成长,别说你在创业

十六、TCP三次握手&Render Tree页面渲染=>从输入URL到页面显示的过程?

十七、前端知识普及之HTML

十八、[翻译]React最佳实践与实用函数

十九、[翻译]从JS模块化现状阐释选择ES6模块的重要性

二十、前端进阶-让你升级的网络知识

二十一、GitHub 2016 年度开源项目报告导读

二十二、正则之基本入门

二十三、你所不知道的 Console

二十四、谈谈React



前端圈--打造专业的前端技术会议

为web前端开发者提供技术分享和交流的平台

打造一个良好的前端圈生态,推动web标准化的发展

官网:http://fequan.com

微博:fequancom | QQ群:41378087


长按二维码关注我们

投稿:content@fequan.com

赞助合作:apply@fequan.com

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存