深拷贝和浅拷贝的理解及其实现方式
深拷贝和浅拷贝
一、概念
浅拷贝:只拷贝最外面一层额数据;更深乘次的对象,只拷贝引用;
深拷贝:拷贝多层数据;每一层级别的数据都会拷贝;
拷贝引用的时候,是属于传址,而非传值;
深拷贝和浅拷贝主要针对的是对象的属性是对象(引⽤类型),浅拷贝只复制指向某个对象的指针,而不复制对象本身,新旧对象还是共享同一块内存。但深拷贝会另外创造一个一模一样的对象,新对象跟原对象不共享内存,修改新对象不会改变原对象。
二、 浅拷贝的实现方式
-
方式1:直接赋值
-
var c = { name:'tom', age:20 } var d = c; c.name = 'jack'; console.log(d);
-
-
方式2:使用 for in
-
var obj1 = { name: 'tom', age: 20, info: { msg: '是一个中国人' } } var obj2 = {}; //for in for (var k in obj1) { obj2[k] = obj1[k]; } console.log(obj2); obj1.info.msg = '虽然是个外国人,但是很向往中国'; console.log(obj2);
-
-
方式3:使用
Object.assign(新数据,老数据)
-
var obj1 = { name:'tom', age:20, info:{ msg:'开心每一天' } } var obj2 = {}; obj2=Object.assign(obj2,obj1); console.log(obj2); obj1.info.msg = '学习使你倍感快乐'; console.log(obj2);
-
-
方式4:函数库lodash的_.clone方法
该函数库也有提供 _.clone 用来做 Shallow Copy,后面我们会再介绍利用这个库实现深拷贝。
-
var _ = require('lodash'); var obj1 = { a: 1, b: { f: { g: 1 } }, c: [1, 2, 3] }; var obj2 = _.clone(obj1); console.log(obj1.b.f === obj2.b.f);// true
-
-
方式5:spread运算符 ...
spread运算符是一个 es6/es2015 特性,它提供了一种非常方便的方式来执行浅拷贝,这与 Object.assign() 的功能相同。它不复制继承的属性或类的属性,但是它会复制ES6的 symbols 属性。
-
let obj1 = { name: 'luck', say:{x:100,y:100}} let obj2= {...obj1} obj1.say.x = 200; obj1.name = 'tom' console.log('obj2',obj2) // obj2 { name: 'luck', say: { x: 200, y: 100 } }
-
-
方式6:
Array.prototype.slice()
-
let arr = [ 1, 3, {username: 'luck'} ]; let arr3 = arr.slice(); arr3[2].username = 'tom' console.log(arr); // [ 1, 3, { username: 'tom' } ]
-
-
方式7:
Array.prototype.concat()
-
let arr = [ 1, 3, {username: 'luck'} ]; let arr2 = arr.concat(); arr2[2].username = 'tom'; console.log(arr); //[ 1, 3, { username: 'tom' } ]
-
三、深拷贝的实现
-
方式1:
递归函数
-
//写一个递归函数,实现检车每一个数据,如果是基本数据类型,则直接添加,如果是引用数据 类型,则递归引用数据类型,把里面的数据变成基本数据类型添加进去; var obj1 = { name:'tom', age:20, info:{ msg:'多看多练' }, color:['red','green','blue'] }; var obj2 = {}; deepCopy(obj2,obj1); console.log(obj2); obj1.info.msg ='不练就不会'; console.log(obj2); function deepCopy(newObj,oldObj) { for(var k in oldObj) { //获取数据 var item = oldObj[k]; if(item instanceof Array) { //判断item是否为为对象 newObj[k] = []; deepCopy(newObj[k],item); }else if(item instanceof Object){ //判断item是否为为对象 newObj[k]= {}; deepCopy(newObj[k],item); }else{ //简单数据类型,直接赋值 newObj[k] = item; } } }
-
-
方式2:
JSON.parse()|JSON.stringify()
;-
let arr = [ 1, 3, {username: 'kobe'} ]; let arr4 = JSON.parse(JSON.stringify(arr)); arr4[2].username = 'duncan'; console.log(arr, arr4)
-
这也是利用 JSON.stringify 将对象转成 JSON 字符串,再用 JSON.parse 把字符串解析成对象,一去一来,新的对象产生了,而且堆内存中会开辟出一个新的区域存放新对象,实现深拷贝。
-
这种方法虽然可以实现数组或对象深拷贝,但不能处理函数和正则,因为这两者基于 JSON.stringify 和 JSON.parse 处理后,得到的正则就不再是正则(变为空对象),得到的函数就不再是函数(变为null)了。
-
例如下面的例子:
-
let arr = [ 1, 3, {username: 'luck'}, function(){} ]; let arr4 = JSON.parse(JSON.stringify(arr)); arr4[2].username = 'tom'; console.log(arr, arr4)
-
-
方式3:
函数库 lodash 的 _.cloneDeep 方法
-
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
-
-
方式4:
jQuery.extend() 方法
-
$.extend(deepCopy, target, object1, [objectN]) //第一个参数为true,就是深拷贝 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
-