读懂javascript深拷贝与浅拷贝
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深拷贝与浅拷贝的更多相关文章
- javascript 深拷贝与浅拷贝
javascript 深拷贝与浅拷贝 深拷贝与浅拷贝 赋值和深/浅拷贝的区别 浅拷贝的实现方式 1.Object.assign() 2.函数库lodash的_.clone方法 3.展开运算符... 4 ...
- javaScript深拷贝和浅拷贝简单梳理
在了解深拷贝和浅拷贝之前,我们先梳理一下: JavaScript中,分为基本数据类型(原始值)和复杂类型(对象),同时它们各自的数据类型细分下又有好几种数据类型 基本数据类型 数字Number 字符串 ...
- JavaScript深拷贝与浅拷贝的理解
个人是这么理解深拷贝和浅拷贝的:就是假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力. 一起看看我举的浅拷贝栗子: let ...
- JavaScript深拷贝与浅拷贝的区别和实现方式
如何区分深拷贝和浅拷贝呢,简单来说对象B拷贝了对象A,如果对象A和对象B共用一个对象,对象B改变对象A跟着改变这就是浅拷贝:但如果对象B拷贝了对象A,但是对象A和对象B是分开的,那么就是深拷贝 基本数 ...
- javaScript 深拷贝、浅拷贝
在 JS 中有一些基本类型像是Number.String.Boolean,而对象就是像这样的东西{ name: 'Larry', skill: 'Node.js' },对象跟基本类型最大的不同就在于他 ...
- JavaScript深拷贝和浅拷贝
1. 基本类型 和 对象类型 他们最大的区别就是在于他们的传值方式. 基本类型是传值 对象类型就是传引用. 这里复制一份obj叫做obj2, 这里修改了obj2的b为100 同时也修改了obj1.b. ...
- JS JavaScript深拷贝、浅拷贝
浅拷贝:浅拷贝只是复制了内存地址,如果原地址中的对象改变了,浅拷贝出来的对象也会相应改变. 深拷贝:开辟了一块新的内存存放地址和地址指向的对象,原地址的任何对象改变了,深拷贝出来的对象不变. 浅拷贝数 ...
- 小白神器 - 两篇博客读懂JavaScript (一基础篇)
JavaScript是一门编程语言,浏览器内置了JavaScript语言的解释器,所以在浏览器上按照JavaScript语言的规则编写相应代码之,浏览器可以解释并做出相应的处理. 一. 编写格式 1, ...
- 十分钟读懂JavaScript原型和原型链
原型(prototype)这个词来自拉丁文的词proto,意谓“最初的”,意义是形式或模型.在JavaScript中,原型的探索也有很多有趣的地方,接下来跟随我的脚步去看看吧. 原型对象释义 每一个构 ...
随机推荐
- Jersey +jetty 实现微服务(一)
微服务: 传输协议基本都是http,数据序列化,协议层目前protocol buffer,Json, xml,Java序列化,最多使用Json.实现以上二点并不难,spring 就可以,但spr ...
- CSS 样式书写规范+特殊符号
虽然我只是刚踏入web前端开发圈子.在一次次任务里头,我发觉每一次的css命名都有所不同和不知所措.脑海就诞生了一个想法--模仿大神的css命名样式. 毕竟日后工作上,是需要多个成员共同协作的.如果没 ...
- web开发之负载均衡的简单架构
负载均衡 负载均衡的核心思想就是:请求分担 最简单的配置: 一台负载均衡服务器 两台webserver服务器 两台webserver服务器需要配置相同的服务器环境,设置相同的域名指向 负载均衡服务器需 ...
- 用caffe一步一步实现人脸检测
学习深度学习已有一段时间了,总想着拿它做点什么,今天终于完成了一个基于caffe的人脸检测,这篇博文将告诉你怎样通过caffe一步步实现人脸检测.本文主要参考唐宇迪老师的教程,在这里感谢老师的辛勤付出 ...
- 【javascript】继承
1. js 其实是一个非面向对象的语言,通过对象的深浅复制完成继承 2. 继承方法 继承的方法有两种 1)prototype 原型模式 举个例子 var Animal = function () { ...
- ZOJ - 3469 Food Delivery (区间dp)
When we are focusing on solving problems, we usually prefer to stay in front of computers rather tha ...
- 分布式事务,EventBus 解决方案:CAP【中文文档】
前言 很多同学想对CAP的机制以及用法等想有一个详细的了解,所以花了将近两周时间写了这份中文的CAP文档,对 CAP 还不知道的同学可以先看一下这篇文章. 本文档为 CAP 文献(Wiki),本文献同 ...
- SSH项目过一段时间之后再访问会报一次Could not open Hibernate session for transaction 异常,Caused by: com.mysql.jdbc.CommunicationsException: Communications link failure due to underlyi,再重新方法即可访问成功(通常出现在过了一晚之后再去访问系统)
前端时间到客户那去进行项目的上线测试,将项目部署好之后,运行都是正常的,可是每到了第二天早上访问的时候,就会报一个Could not open Hibernate session for transa ...
- js中this的指向总结
// this要在执行时才能确认值,定义时无法确认.下面是常见的几种this指向. //1.在构造函数执行 function Obj(name,age){ //1.实例化时:会创建一个 空对象 ...
- 这是个巧妙的表达式: a ˆ= b ˆ= a ˆ= b 它不需要临时变量就可 以交换 a 和 b 的值。
这不具有可移植性.它试图在序列点之间两次修改变量 a, 而这是无定义的. 例如,有人报告如下代码: int a = 123, b = 7654; a ^= b ^= a ^= b; 在 SCO 优化 ...