1. 认识深拷贝和浅拷贝

  javascript中一般有按值传递和按引用传递两种复制,按值传递的是基本数据类型(Number,String,Boolean,Null,Undefined),一般存放于内存中的栈区,存取速度快,存放量小;按引用传递的是引用类型(Object,Array,Function,Symbol),一般存放与内存中的堆区,存取速度慢,存放量大,其引用指针存于栈区,并指向引用本身。

  深拷贝和浅拷贝是相对于引用类型而言的:

  浅拷贝: 指两个js 对象指向同一个内存地址,其中一个改变会影响另一个;

  深拷贝: 指复制后的新对象重新指向一个新的内存地址,两个对象改变互不影响。

2. 浅拷贝

  浅拷贝常用的方法如下:

  1. 简单的赋值操作:

var arr = [1,2,3];
var newarr = arr;
newarr[0] = "one";
console.log(arr); // ["one", 2, 3]
console.log(newarr); // ["one", 2, 3]
console.log(arr==newarr);  // true
console.log(arr===newarr);  // true

  2. Object.assign()方法是ES6的新函数, 可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象。拷贝的是对象的属性的引用,而不是对象本身,但是也可以实现一层深拷贝:

var obj = { a: {a: "hello"}, b: 33 };
var newObj = Object.assign({}, obj);
newObj.a.a = "hello world";
console.log(obj); // { a: {a: "hello world"}, b: 33 };
console.log(newObj); // { a: {a: "hello world"}, b: 33 };
console.log(obj.a.a==newObj.a.a);  // true
console.log(obj.a.a===newObj.a.a);  // true

  3.$.extend({},obj)使用递归思路实现了浅拷贝和深拷贝,第一个参数类型为Boolean,当为false的时候必须省略不写则是浅拷贝,当为true的时候为深拷贝:

var obj = { a: {a: "hello"}, b: 33 };
var newObj = $.extend({}, obj);
newObj.a.a = "hello world";
console.log(obj); // { a: {a: "hello world"}, b: 33 };
console.log(newObj); // { a: {a: "hello world"}, b: 33 };
console.log(obj.a.a==newObj.a.a);  // true
console.log(obj.a.a===newObj.a.a);  // true

  浅拷贝是我们经常使用的操作一些对象或数组的有效方法,具体的使用需要结合实际场景来合理的使用,还要考虑一些兼容性的问题,但是在大多实际情景下我们需要使用更多的是深拷贝,尤其是在各种MVVM框架中,引入了状态管理,更多的体现在数据流中希望我们不改变原对象,这样的话实现深拷贝就显得尤为重要。

3. 深拷贝

  简单深拷贝常用的方法如下(一维的数据结构):

  1.手动的赋值操作:

var obj = { a: 10, b: 20};
var newObj = { a: obj.a, b: obj.b};
newObj.b = 100;
console.log(obj); // { a: 10, b: 20}
console.log(newObj); // { a: 10, b: 100};
console.log(obj == newObj); // false
console.log(obj === newObj); // false

  2.Object.assign()方法是ES6的新函数,只能简单的复制一层属性到目标对象,还得考虑兼容性:

var obj = { a: {a: "hello"}, b: 33 };
var newObj = Object.assign({}, obj);
newObj.b = 100;
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello", b: 100 };
console.log(obj==newObj);  // false
console.log(obj===newObj);  // false

  复杂深拷贝常用的方法如下(二维的数据结构及以上):

  1.JSON.parse(JSON.stringify(obj))是最简单粗暴的深拷贝,能够处理JSON格式的所有数据类型,但是对于正则表达式类型、函数类型等无法进行深拷贝,而且会直接丢失相应的值,还有就是它会抛弃对象的constructor。也就是深拷贝之后,不管这个对象原来的构造函数是什么,在深拷贝之后都会变成Object。同时如果对象中存在循环引用的情况也无法正确处理:

var obj = { a: {a: "hello"}, b: 33 };
var newObj = JSON.parse(JSON.stringify(obj));
newObj.b = "hello world";
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello world", b: 33};
console.log(obj==newObj);  // false
console.log(obj===newObj);  // false

  2.$.extend(true,{},obj)使用递归思路可以实现深拷贝,要求第一个参数必须为true:

var obj = { a: {a: "hello"}, b: 33 };
var newObj = $.extend(true, {}, obj);
newObj.a.a = "hello world";
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello world", b: 33 };
console.log(obj==newObj);  // false
console.log(obj===newObj);  // false

  3. lodash中的_.clone(obj, true)等价于_.cloneDeep(obj) 两个方法,lodash花了大量的代码来实现 ES6 引入的大量新的标准对象,并针对存在环的对象的处理也是非常出色的,因此对于深拷贝来说lodash和其他库相比最友好:

var obj = { a: {a: "hello"}, b: 33 };
var newObj = _.cloneDeep(obj);
newObj.a.a = "hello world";
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello world", b: 33 };
console.log(obj==newObj);  // false
console.log(obj===newObj);  // false

  4. 自己实现一个简单的深拷贝deepClone():

function deepClone(obj){
if(typeof obj !== "object") return;
let newObj = obj instanceof Array ? [] : {};
for(let key in obj){
if(obj.hasOwnProperty(key)){
newObj[key] = typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key];
}
}
return newObj;
}
let obj = {a: 11, b: function(){}, c: {d: 22}};
deepClone(obj); // {a: 11, b: f(), c: {d: 22}};

  对于深拷贝来说最常用的就是这些方法,当然还有其他的一些库,比如deepCopy等,此外数组常用contact和slice来实现深拷贝,不同的方法有其最好的适用环境,还是那句话:"离开场景谈性能就是耍流氓",下面用数据具体分析一下一维数据结构和二维数据结构在不同方法下的性能对比。

4.深拷贝不同方法的性能对比

  深拷贝一维数据结构用时对比:

var obj = [];
for (var i = 0; i < 100; i++) {
obj[i] = Math.random();
}
console.time("assign");
var newObj = Object.assign({}, obj);
console.timeEnd("assign");
console.time("JSON.parse(JSON.stringify())");
var newObj = JSON.parse(JSON.stringify(obj));
console.timeEnd("JSON.parse(JSON.stringify())");
console.time("$.extend");
var newObj = $.extend(true, {}, obj);
console.timeEnd("$.extend");
console.time("Loadsh.cloneDeep");
var newObj = _.cloneDeep(obj);
console.timeEnd("Loadsh.cloneDeep");

  经过多次实验分析发现,一维数据结构的深拷贝方法性能最佳的为Object.assign();

  深拷贝二维数据结构用时对比:

var obj = [];
for (var i = 0; i < 100; i++) {
obj[i] = {};
for (var j = 0; j < 100; j++) {
obj[i][j] = Math.random();
}
}
console.time("JSON.parse(JSON.stringify())");
var newObj = JSON.parse(JSON.stringify(obj));
console.timeEnd("JSON.parse(JSON.stringify())");
console.time("$.extend");
var newObj = $.extend(true, {}, obj);
console.timeEnd("$.extend");
console.time("Loadsh.cloneDeep");
var newObj = _.cloneDeep(obj);
console.timeEnd("Loadsh.cloneDeep");

经过多次实验分析发现,二维数据结构的深拷贝方法性能最佳的为JSON.parse(JSON.stringify())

5.总结

  一维数据结构的深拷贝方法建议使用:Object.assign();

  二维数据结构及以上的深拷贝方法建议使用:JSON.parse(JSON.stringify());

  特别复杂的数据结构的深拷贝方法建议使用:Loadsh.cloneDeep();

读懂javascript深拷贝与浅拷贝的更多相关文章

  1. javascript 深拷贝与浅拷贝

    javascript 深拷贝与浅拷贝 深拷贝与浅拷贝 赋值和深/浅拷贝的区别 浅拷贝的实现方式 1.Object.assign() 2.函数库lodash的_.clone方法 3.展开运算符... 4 ...

  2. javaScript深拷贝和浅拷贝简单梳理

    在了解深拷贝和浅拷贝之前,我们先梳理一下: JavaScript中,分为基本数据类型(原始值)和复杂类型(对象),同时它们各自的数据类型细分下又有好几种数据类型 基本数据类型 数字Number 字符串 ...

  3. JavaScript深拷贝与浅拷贝的理解

    个人是这么理解深拷贝和浅拷贝的:就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力. 一起看看我举的浅拷贝栗子: let ...

  4. JavaScript深拷贝与浅拷贝的区别和实现方式

    如何区分深拷贝和浅拷贝呢,简单来说对象B拷贝了对象A,如果对象A和对象B共用一个对象,对象B改变对象A跟着改变这就是浅拷贝:但如果对象B拷贝了对象A,但是对象A和对象B是分开的,那么就是深拷贝 基本数 ...

  5. javaScript 深拷贝、浅拷贝

    在 JS 中有一些基本类型像是Number.String.Boolean,而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他 ...

  6. JavaScript深拷贝和浅拷贝

    1. 基本类型 和 对象类型 他们最大的区别就是在于他们的传值方式. 基本类型是传值 对象类型就是传引用. 这里复制一份obj叫做obj2, 这里修改了obj2的b为100 同时也修改了obj1.b. ...

  7. JS JavaScript深拷贝、浅拷贝

    浅拷贝:浅拷贝只是复制了内存地址,如果原地址中的对象改变了,浅拷贝出来的对象也会相应改变. 深拷贝:开辟了一块新的内存存放地址和地址指向的对象,原地址的任何对象改变了,深拷贝出来的对象不变. 浅拷贝数 ...

  8. 小白神器 - 两篇博客读懂JavaScript (一基础篇)

    JavaScript是一门编程语言,浏览器内置了JavaScript语言的解释器,所以在浏览器上按照JavaScript语言的规则编写相应代码之,浏览器可以解释并做出相应的处理. 一. 编写格式 1, ...

  9. 十分钟读懂JavaScript原型和原型链

    原型(prototype)这个词来自拉丁文的词proto,意谓“最初的”,意义是形式或模型.在JavaScript中,原型的探索也有很多有趣的地方,接下来跟随我的脚步去看看吧. 原型对象释义 每一个构 ...

随机推荐

  1. Java的基本数据类型和运算

    编码 ASCII--0~127  65-A  97-a 西欧码表---ISO-8859-1---0-255---1个字节 gb2312----0-65535---gbk---2个字节 Unicode编 ...

  2. 关于sessionStorage的移动端兼容问题

    最近在开发移动端项目时,需要用到的本地存储的地方不少.都是一些只要记住当前打开窗口的用户数据就行,所以我选择用的sessionStorage.使用场景如下: A.html页面需要记录一条数据{a:1, ...

  3. linux文件系统及bash基础特性

    linux文件系统 一.根文件系统 linux被识别的第一个被称为根之间关联的文件系统叫做根文件系统(rootfs),其他分区要想被读到,需要挂载到根目录的某个挂载点(根的子目录)上.根文件系统至关重 ...

  4. (转)Spring boot——logback.xml 配置详解(二)

    文章转载自:http://aub.iteye.com/blog/1101260,在此对作者的辛苦表示感谢! 1 根节点<configuration>包含的属性 scan: 当此属性设置为t ...

  5. CSS样式----CSS的继承性和层叠性(图文详解)

    CSS的继承性 我们来看下面这样的代码,来引入继承性: 上方代码中,我们给div标签增加红色属性,却发现,div里的每一个子标签<p>也增加了红色属性.于是我们得到这样的结论: 有一些属性 ...

  6. node.js fs.open 和 fs.write 读取文件和改写文件

    Node.js的文件系统的Api //公共引用 var fs = require('fs'), path = require('path'); 1.读取文件readFile函数 //readFile( ...

  7. 阿里云有对手了!CDN横评:腾讯云优势明显

    如今,云计算产品越来越多,像国内的BAT三大巨头都提供了云主机(腾讯云CVM.阿里云ECS.百度云BCC),另外还有存储.数据库.安全等相关云服务.在这其中,CDN也是一项重要的云服务,CDN指的是内 ...

  8. C++ STL 优先队列详解

    一.解释: 优先队列是队列的一种,不过它可以按照自定义的一种方式(数据的优先级)来对队列中的数据进行动态的排序,每次的push和pop操作,队列都会动态的调整,以达到我们预期的方式来存储. 例如,将元 ...

  9. SMBLoris windows拒绝服务漏洞

    在美国拉斯维加斯举行的2017年度DEF CON黑客大会上,安全研究人员公布了Windows系统上的一个长达20年没有发现的漏洞,该漏洞名为"SMBLoris",黑客可以轻松的使用 ...

  10. NYOJ--95--multiset--众数问题

    /* Name: NYOJ--95--众数问题 Date: 20/04/17 16:02 Description: multiset水过 */ #include<set> #include ...