什么是对象的拷贝?
将一个对象赋值给另一个对象, 咱们称之为对象的拷贝javascript
什么是深拷贝, 什么是浅拷贝?
咱们假设将A对象赋值给B对象前端
浅拷贝是指, 修改B对象的属性和方法会影响到A对象的属性和方法, 咱们称之为浅拷贝java
如下两种状况都属于浅拷贝:数组
1、默认状况下对象之间的直接赋值都是浅拷贝函数
let A = { name: 'zyx', age: 20 } let B = A console.log(B) // {name: "zyx", age: 20} //修改B的 name 属性 B.name = 'ls' //A 也收到影响 console.log(A) // {name: "ls", age: 20} console.log(B) // {name: "ls", age: 20}
赋值操做(包括对象做为参数、返回值),不会开辟新的内存空间,他只是赋值了对象的引用.也就是除了B这个名字以外,没有其余的内存开销,修改了A也就影响了B,修改了B,也就影响了A.优化
如图所示:prototype
2、若是对象的属性包含了引用数据类型(数组、对象),那么哪怕不是直接赋值操做,而是开辟了一层新的内存空间,也就是说只拷贝了A对象的一层,这仍然属于浅拷贝。code
let A = { name: 'ls', age: 20, hobbies: ['dance','basketball','read'], dogs:{ name: '大黄', color: 'yellow' } } let B = {} //定义一个函数,把A对象的属性复制一份给B function extend(obj1,obj2){ for(var key in obj1){ obj2[key] = obj1[key] } } extend(A,B) //修改B对象中的引用类型数据 ,A对象也收到影响 B.dogs.color = 'red' B.hobbies[0] = 'sing' console.log(B) console.log(A)
运行截图以下:修改B对象中的引用类型数据 ,A对象也收到影响,属于浅拷贝对象
三、ES6中新增的 Object.assign() 也是对象的浅拷贝blog
Object.assign
方法用于对象的合并,将源对象(source)的全部可枚举属性,复制到目标对象(target)。 Object.assign
方法的第一个参数是目标对象,后面的参数都是源对象。 注意,若是目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
const obj1 = {a: {b: 1}}; const obj2 = Object.assign({}, obj1); obj1.a.b = 2; obj2.a.b // 2
上面代码中,源对象obj1
的a
属性的值是一个对象,Object.assign
拷贝获得的是这个对象的引用。这个对象的任何变化,都会反映到目标对象上面。
四、扩展运算符(...)
利用扩展运算符能够在构造字面量对象时,进行克隆或者属性拷贝 ,属于浅拷贝
var obj = {a:1,b:{c:1}} var obj2 = {...obj}; obj.a=2; console.log(obj); //{a:2,b:{c:1}} console.log(obj2); //{a:1,b:{c:1}} obj.b.c = 2; console.log(obj); //{a:2,b:{c:2}} console.log(obj2); //{a:1,b:{c:2}}
五、Array.prototype.slice()
slice() 方法返回一个新的数组对象,这一对象是一个由 begin和 end(不包括end)决定的原数组的浅拷贝。原始数组不会被改变。
深拷贝是指, 修改B对象的属性和方法不会影响到A对象的属性和方法, 咱们称之为深拷贝
如下两种状况都属于深拷贝:
一、默认状况下一个对象的属性若是是基本数据类型, 那么进行复制(拷贝),都是深拷贝
若是A对象的属性都是基本数据类型(Number、String等),此时要想深拷贝一份A给B,该怎么作呢,在这种要拷贝的对象A只有基本类型的数据时,只须要在内存中开辟一块空间存储B就好了。
let A = { name: 'zyx', age: 20 } let B = {} //定义一个函数,把A对象的属性复制一份给B function extend(obj1,obj2){ for(var key in obj1){ obj2[key] = obj1[key] } } extend(A,B) console.log(B) // {name: "zyx", age: 20} B.name = 'ls' console.log(B) // {name: "ls", age: 20} console.log(A) // {name: "zyx", age: 20}
这样就实现了深拷贝,以下图所示:
二、若是要拷贝的对象自己又包含了引用数据类型,即对象又包含数组或者对象,层层嵌套的状况下,想要实现对象的深拷贝,能够采用递归的方式进行深拷贝。
let A = { name: 'ls', age: 20, hobbies: ['dance','basketball','read'], dogs:{ name: '大黄', color: 'yellow' } } let B = {} //定义一个函数,把A对象的属性复制一份给B function extend(obj1,obj2){ for(var key in obj1){ var item = obj1[key] if(item instanceof Array){ obj2[key] = [] extend(item,obj2[key]) }else if(item instanceof Object){ obj2[key] = {} extend(item,obj2[key]) }else{ obj2[key] = item } } } extend(A,B) B.dogs.color = 'red' B.hobbies[0] = 'sing' console.log(B) console.log(A)
运行发现,修改B对象的引用数据类型,不会影响到A对象,完成深拷贝
咱们能够对深拷贝的代码进行封装优化
function deepClone(obj){ let cloneObj = {} for(let key in obj){ if(typeof obj[key] === 'object'){ cloneObj[key] = deepClone(obj[key]) }else{ cloneObj[key] = obj[key] } } return cloneObj }
三、经过JSON.stringify实现深拷贝
JSON.stringify()是目前前端开发过程当中最经常使用的深拷贝方式,原理是把一个对象序列化成为一个JSON字符串,将对象的内容转换成字符串的形式再保存在磁盘上,再用JSON.parse()反序列化将JSON字符串变成一个新的对象。
var obj1 = { a:1, b:[1,2,3] } var str = JSON.stringify(obj1) var obj2 = JSON.parse(str) console.log(obj2); //{a:1,b:[1,2,3]} obj1.a=2 obj1.b.push(4); console.log(obj1); //{a:2,b:[1,2,3,4]} console.log(obj2); //{a:1,b:[1,2,3]}