什么是浅拷贝深拷贝(如何实现浅拷贝)
什么是浅拷贝深拷贝(如何实现浅拷贝)let a = { age: 1 jobs: { first: 'FE' } } let b = JSON.parse(JSON.stringify(a)) a.jobs.first = 'native' console.log(b.jobs.first) // FE 但是该方法也是有局限性的:这个问题通常可以通过JSON.parse(JSON.stringify(object))来解决。let a = { age: 1 } let b = { ...a } a.age = 2 console.log(b.age) // 1 通常浅拷贝就能解决大部分问题了,但是当我们遇到如下情况就可能需要使用到深拷贝了let a = { age: 1 jobs: { first: 'FE' } } let b = { ...a } a.jobs.first = 'native' co
问题:什么是浅拷贝?如何实现浅拷贝?什么是深拷贝?如何实现深拷贝?我们了解了对象类型在赋值的过程中其实是复制了地址,从而会导致改变了一方其他也都被改变的情况。通常在开发中我们不希望出现这样的问题,我们可以使用浅拷贝来解决这个情况。
let a = { age: 1 } let b = a a.age = 2 console.log(b.age) // 2
浅拷贝
首先可以通过Object.assign来解决这个问题,很多人认为这个函数是用来深拷贝的。其实并不是,Object.assign只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝。
let a = { age: 1 } let b = Object.assign({} a) a.age = 2 console.log(b.age) // 1
另外我们还可以通过展开运算符(...)来实现浅拷贝
let a = { age: 1 } let b = { ...a } a.age = 2 console.log(b.age) // 1
通常浅拷贝就能解决大部分问题了,但是当我们遇到如下情况就可能需要使用到深拷贝了
let a = { age: 1 jobs: { first: 'FE' } } let b = { ...a } a.jobs.first = 'native' console.log(b.jobs.first) // native
浅拷贝只解决了第一层的问题,如果接下去的值中还有对象的话(jobs就是),那么就又回到最开始的话题了,两者享有相同的地址。要解决这个问题,我们就得使用深拷贝了。
深拷贝
这个问题通常可以通过JSON.parse(JSON.stringify(object))来解决。
let a = { age: 1 jobs: { first: 'FE' } } let b = JSON.parse(JSON.stringify(a)) a.jobs.first = 'native' console.log(b.jobs.first) // FE
但是该方法也是有局限性的:
- 会忽略undefined
- 会忽略symbol
- 不能序列化函数
- 不能解决循环引用的对象
let obj = { a: 1 b: { c: 2 d: 3 } } obj.c = obj.b obj.e = obj.a obj.b.c = obj.c obj.b.d = obj.b obj.b.e = obj.b.c let newObj = JSON.parse(JSON.stringify(obj)) console.log(newObj)
如果你有这么一个循环引用对象,你会发现并不能通过该方法实现深拷贝
在遇到函数、undefined或者symbol的时候,该对象也不能正常的序列化
let a = { age: undefined sex: Symbol('male') jobs: function() {} name: 'vue' } let b = JSON.parse(JSON.stringify(a)) console.log(b) // {name: "vue"}
你会发现在上述情况中,该方法会忽略掉函数和undefined。
但是在通常情况下,复杂数据都是可以序列化的,所以这个函数可以解决大部分问题。
当然你可能想自己来实现一个深拷贝,但是其实实现一个深拷贝是很困难的,需要我们考虑好多种边界情况,比如原型链如何处理、DOM 如何处理等等,所以这里我们实现的深拷贝只是简易版,并且我其实更推荐使用lodash 的深拷贝函数
function deepClone(obj) { function isObject(o) { return (typeof o === 'object' || typeof o === 'function') && o !== null } if (!isObject(obj)) { throw new Error('非对象') } let isArray = Array.isArray(obj) let newObj = isArray ? [...obj] : { ...obj } Reflect.ownKeys(newObj).forEach(key => { newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key] }) return newObj } let obj = { a: [1 2 3] b: { c: 2 d: 3 } } let newObj = deepClone(obj) newObj.b.c = 1 console.log(obj.b.c) // 2