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. Aspose.words 书签定位

    1. 简介 Aspose.words 可以在不使用 Microsoft.Word 的情况下生成.修改.转换.打印文档.不依赖office组件,这一点给我们提供了极大的便利性,可以简单的引入 DLL(D ...

  2. JMeter Http请求POST提交token失败,取样器结果Response Code 415

    Jmeter脚本,http请求以post方式提交token,执行脚本在察看结果中显示失败,取样器结果响应状态Response Code 415,如图:

  3. Python面向对象编程(一)

    1.什么是面向对象 面向对象(oop)是一种抽象的方法来理解这个世界,世间万物都可以抽象成一个对象,一切事物都是由对象构成的.应用在编程中,是一种开发程序的方法,它将对象作为程序的基本单元. 2.面向 ...

  4. GIT - 代码管理工具之命令集

    GIT 是一个快速.可扩展的分布式版本控制系统,它具有极为丰富的命令集,对内部系统提供了高级操作和完全访问.它会把你的每次提交的文件的全部内容都会记录下来. GIT特点 速度 简单的设计 对非线性开发 ...

  5. Actor模型原理

    1.Actor模型 在使用Java进行并发编程时需要特别的关注锁和内存原子性等一系列线程问题,而Actor模型内部的状态由它自己维护即它内部数据只能由它自己修改(通过消息传递来进行状态修改),所以使用 ...

  6. Python-cookies

    附新手工具: python新手工具下载链接:http://pan.baidu.com/s/1eS8WMR4 密码:7eso 注册码:http://idea.liyang.io py和编辑器安装教程链接 ...

  7. SpringBoot上传任意文件功能的实现

    一.pom文件依赖的添加 <dependencies> <dependency> <groupId>org.springframework.boot</gro ...

  8. javascript编程解决黑化的牛牛问题

    问题描述 时间限制:1秒 空间限制:32768K 牛牛变得黑化了,想要摧毁掉地球.但他忘记了开启地球毁灭器的密码.牛牛手里有一个字符串S,牛牛还记得从S中去掉一个字符就恰好是正确的密码,请你帮牛牛求出 ...

  9. Nlpir Parser敏感词搜索灵玖语义技术应用

    近年来随着网络技术的飞速发展和用户的剧烈增长,网络传输数据量越来越大,网络用语越来越趋于多样化.如何快速的屏蔽用户的不当言论.过滤用户发表内容中的非法词汇已成为关键词匹配领域的一项重大难题. 目前主要 ...

  10. year:2017 month:7 day:20

    2017-07-20 JavaScript(Dom) 1:获取节点对象 document.getElementById("html元素的id") document.getEleme ...