浅拷贝
因为对象是引用类型,如果被引用,引用改变了,它就改变了。
浅拷贝的常见方法,如数组的concat方法,对象的Object.assign() 方法。展开运算符...,for循环。
// 数组的浅拷贝
let arr = ['张三', 18]
let newArr = []
通过循环进行的浅拷贝
for (let i = 0; i < arr.length; i++) {
newArr[i] = arr[i]
}
// // 通过concat方法
// newArr = [].concat(arr)
// // 通过展开运算符...
// newArr=[...arr]
newArr[0] = '李四'
console.log(arr) // [ '张三', 18 ]
console.log(newArr) // [ '李四', 18 ]
// 对象的浅拷贝
let obj = {
name: '张三',
age: 18
}
let newObj = {}
// 通过循环进行的浅拷贝
for (let key in obj) {
newObj[key] = obj[key]
}
// // 通过Object.assgin()方法
// Object.assign(newObj, obj)
// // 通过展开运算符...
// newObj={...obj}
newObj.name = '李四'
console.log(obj) // { name: '张三', age: 18 }
console.log(newObj) // { name: '李四', age: 18 }
以上代码中,我们使用了常用的几种方法浅拷贝了一个新的数组和对象。虽然现在看上去是没有问题的,但是对象是多层嵌套的话,拷贝之后各个值的指针还是指向相同的存储地址,更改新对象值时,被拷贝的老对象的值也会随之改变。如下:
let obj = {
son: {
name: '张三'
}
}
let newObj = { ...obj }
newObj.son.name = '李四'
console.log(obj); // { son: { name: '李四' } }
console.log(newObj); // { son: { name: '李四' } }
所以,拷贝时需要注意以下是否是多层嵌套的,如果是,则需要进行深拷贝了。
深拷贝
深拷贝的方式也有很多中,其中有递归的方式(建议记住,面试时有可能用到哦), JSON 序列化(不推荐使用),lodash 的 cloneDeep(推荐使用)。
let obj = {
son: {
name: '张三'
}
}
let newObj = {}
// 通过递归实现
function cloneDeep(obj) {
// 判断一下传递的值是否是数组或在对象
if (typeof obj === 'object') {
// 判断一下传递的值是对象还是数组,如果不是直接return
const newObj = Array.isArray(obj) ? [] : {}
for (const key in obj) {
// 判断一下每一个值是否是数组或者对象,是就再次调用本身
if (typeof (obj[key]) === 'object') {
newObj[key] = cloneDeep(obj[key])
} else {
newObj[key] = obj[key]
}
}
return newObj
} else {
return console.log('不是数组或者对象');
}
}
newObj = cloneDeep(obj)
// // JSON 序列化
// newObj=JSON.parse(JSON.stringify(obj))
// // lodash 库中的 cloneDeep
// newObj = _.cloneDeep(obj)
newObj.son.name = '李四'
console.log(obj); // { son: { name: '张三' } }
console.log(newObj); // { son: { name: '李四' } }
以上代码中,我们深拷贝了一个新的对象,拷贝之后各个值的指针指向的都是自己的存储地址,更改新对象值时,被拷贝的老对象的值就不会随之改变。
之所以 JSON 序列化的方式不推荐使用,是因为使用时,它会忽略掉function undefined。如下:
let obj = {
name: '张三',
sayName: function () {
console.log(this.name);
},
age: undefined
}
let newObj = JSON.parse(JSON.stringify(obj));
console.log(obj); // { name: '张三', sayName: [Function: sayName], age: undefined }
console.log(newObj); // { name: '张三' }
总结
浅拷贝是一个非常简单的方式,只有拷贝的对象是单层,没有嵌套时可以简单的使用,(不建议使用)。
当然,浅拷贝也是有一定的优点的:
1.简洁易懂,实现起来比较容易;
2.复制的对象是原对象的一份浅表副本,对副本的修改不会影响原对象。
深拷贝的优点是:
- 完全复制了整个对象,对副本的修改不会影响原对象;
- 不受对象层次结构的限制,可以复制多层嵌套的对象。
深拷贝的缺点是: 深拷贝的实现通常比浅拷贝复杂得多,而且会消耗更多的计算资源。对于大型对象或多层嵌套的对象,> 深拷贝的效率和性能可能会变得很低。
深拷贝中,JSON序列化的优点:理解简单,用起来也简单。
缺点:只适用于那些能够被 JSON 直接表示的数据结构,且它会忽略掉 function undefined
最后,lodash的地址