javascript
中通常有按值传递和按引用传递两种复制方式:javascript
按值传递的是基本数据类型(Number,String,Boolean,Null,Undefined,Symbol),通常存放于内存中的栈区,存取速度快,存放量小;java
按引用传递的是引用类型(Object,Array,Function),通常存放与内存中的堆区,存取速度慢,存放量大,其引用指针存于栈区,并指向引用自己。正则表达式
深拷贝和浅拷贝是相对于引用类型而言的:数组
浅拷贝: 指两个js 对象指向同一个内存地址,其中一个改变会影响另外一个;bash
深拷贝: 指复制后的新对象从新指向一个新的内存地址,两个对象改变互不影响。markdown
浅拷贝经常使用的方法以下:数据结构
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 复制代码
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 复制代码
$.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
框架中,引入了状态管理,更多的体如今数据流中但愿咱们不改变原对象,这样的话实现深拷贝就显得尤其重要。框架
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 复制代码
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 复制代码
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 复制代码
$.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 复制代码
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 复制代码
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来实现深拷贝,不一样的方法有其最好的适用环境,仍是那句话:"离开场景谈性能就是耍流氓",下面用数据具体分析一下一维数据结构和二维数据结构在不一样方法下的性能对比。dom
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())
;
一维数据结构的深拷贝方法建议使用:Object.assign()
;函数
二维数据结构及以上的深拷贝方法建议使用:JSON.parse(JSON.stringify())
;
特别复杂的数据结构的深拷贝方法建议使用:Loadsh.cloneDeep()
;
更多精彩内容欢迎关注个人公众号【天道酬勤Lewis】