我们从以下三个方面进行学习浅拷贝和深拷贝。
1.首先了解为什么要学习浅拷贝和深拷贝?
2.浅拷贝的概念和用法,以及优缺点。
3.深拷贝的概念和用法,深拷贝的好处。
下面我们开始学习啦!
(以下代码中 console.log() 后面注释皆为控制台输出结果。)
第一部分:对象赋值问题(学习深浅拷贝的原因)
开发中我们经常需要复制一个对象。如果直接用赋值会有下面问题:
如下面的代码:将对象p1赋值给p2,改变p2里面属性的值,p1里面对应的值也会被改变。
改变原因:因为赋值的时候,是直接拷贝对象栈里面的地址,p1和p2的地址相同,所以修改会将两个一起改变。
<script>
// 创建一个p1对象
const p1 = {
name: '小白',
age: 18
}
const p2 = p1
console.log(p2); //{ name: '小白',age: 18}
p2.name = '小黑'
console.log(p2); //{name: '小黑', age: 18}
console.log(p1); //{name: '小黑', age: 18}
</script>
那么,如果我们只想改变p2里面的,p1不变,我们该怎么避免这种情况呢?
第二部分:浅拷贝
浅拷贝: 将一个对象的所有属性拷贝到另一个对象 (并且改变拷贝的对象, 不影响原始对象)
学习浅拷贝 --- 两个方法
(1)Object.assign()
(2) ... 展开运算符
浅拷贝 核心是 --- 创建新地址
(1)Object.assign() 方法
语法:Object.assign(拷贝的对象,原始对象)
<script>
// 创建一个p1对象
const p1 = {
name: '小白',
age: 18
}
const p2 = {}
Object.assign(p2, p1)
console.log(p2); //{ name: '小白',age: 18}
p2.name = '小黑'
console.log(p2); //{name: '小黑', age: 18}
console.log(p1); //{ name: '小白',age: 18}
</script>
(2) ... 展开运算符方法
语法:{...原始对象}
<script>
// 创建一个p1对象
const p1 = {
name: '小白',
age: 18
}
const p2 = {...p1}
console.log(p2); //{ name: '小白',age: 18}
p2.name = '小黑'
console.log(p2); //{name: '小黑', age: 18}
console.log(p1); //{ name: '小白',age: 18}
</script>
浅拷贝的问题: 当原始对象里面的属性值是复杂数据类型的时候, 浅拷贝就会有bug,浅拷贝没有对这个值创建新地址
简单来说就是浅拷贝: 只能拷贝一层对象,或者一层数组
bug的来源就是:如果原始对象有嵌套对象的时候,那么他还是采用等号赋值, 地址依然一样,在堆里面值只有一份
<script>
// 创建一个p1对象
const p1 = {
name: '小白',
age: 18,
address: {
province: '北京',
citys: '东城区'
}
}
const p2 = {}
Object.assign(p2, p1)
console.log(p2); //{ name: '小白',age: 18,address: {province: '上海', citys: '东城区'}}
p2.name = '小黑'
p2.address.province = '上海'
console.log(p2); //{name: '小黑', age: 18,address: {province: '上海', citys: '东城区'}}
console.log(p1); //{ name: '小白',age: 18,address: {province: '上海', citys: '东城区'}}
</script>
第三部分:深拷贝
深拷贝: 将一个对象的所有属性拷贝到另一个对象 (并且改变拷贝的对象, 也不影响含多层对象的原始对象)
学习深拷贝 --- 三个方法
(1)通过递归实现深拷贝
(2) JSON.stringify() JSON.parse()
(3) lodash
深拷贝 核心是 --- 创建新地址
(1)通过递归实现深拷贝
拓展: 函数递归: 如果一个函数在内部可以调用其本身,那么这个函数就是递归函数
简单理解:函数内部自己调用自己, 这个函数就是递归函数
递归函数的作用和循环效果类似 由于递归很容易发生“栈溢出”错误(stack overflow),所以必须要加退出条件 return
<script>
let num = 1
function fn() {
num++
console.log(num);
//退出条件
if (num > 5) {
return
}
fn()
}
fn()
</script>
递归实现深拷贝:代码如下
<script>
// 1.创建初始对象
const p1 = {
name: '小白',
age: 18,
address: {
province: '北京',
citys: '东城区'
}
}
// 2.递归函数
function deepClone(old) {
const p2 = {}
for (let key in old) {
if (typeof old[key] === 'object') {
p2[key] = deepClone(old[key])
} else {
p2[key] = old[key]
}
}
return p2
}
const p2 = deepClone(p1)
console.log(p2); //{ name: '小白',age: 18,address: {province: '上海', citys: '东城区'}}
// 3.改变拷贝对象里面的 多层对象的值,原始的对象里面的值就不会被改变了
p2.address.province = '上海'
console.log(p2); //{ name: '小白',age: 18,address: {province: '上海', citys: '东城区'}}
console.log(p1); //{ name: '小白',age: 18,address: {province: '北京', citys: '东城区'}}
</script>
(2) JSON.stringify() JSON.parse()方法 (工作中使用)
JSON.stringify() 将对象转换为字符串 JSON字符串
JSON.parse() 转换为复杂数据类型
为什么可以这样写:因为关键就是创建新地址,字符串是普通数据类型,地址在栈里面
JSON.stringify()转换成字符串就是一个新地址
<script>
// 1.创建初始对象
const p1 = {
name: '小白',
age: 18,
address: {
province: '北京',
citys: '东城区'
}
}
// 转换
const p2 = JSON.parse(JSON.stringify(p1))
console.log(p2); //{ name: '小白',age: 18,address: {province: '上海', citys: '东城区'}}
// 3.改变拷贝对象里面的 多层对象的值,原始的对象里面的值就不会被改变了
p2.address.province = '上海'
console.log(p2); //{ name: '小白',age: 18,address: {province: '上海', citys: '东城区'}}
console.log(p1); //{ name: '小白',age: 18,address: {province: '北京', citys: '东城区'}}
</script>
深拷贝的缺陷:
(3) lodash
利用js库 lodash里面的 _.cloneDeep()
lodash工具库:https://www.lodashjs.com/
<script src="./lodash.min.js"></script>
<script>
// 1.创建初始对象
const p1 = {
name: '小白',
age: 18,
address: {
province: '北京',
citys: '东城区'
}
}
// 转换
const p2 = _.cloneDeep(p1) //深拷贝
console.log(p2); //{ name: '小白',age: 18,address: {province: '上海', citys: '东城区'}}
// 3.改变拷贝对象里面的 多层对象的值,原始的对象里面的值就不会被改变了
p2.address.province = '上海'
console.log(p2); //{ name: '小白',age: 18,address: {province: '上海', citys: '东城区'}}
console.log(p1); //{ name: '小白',age: 18,address: {province: '北京', citys: '东城区'}}
</script>