提示:今天为啥更文了呢,因为《间谍过家家》第二季出来了呀,开心开心,会预知未来的狗狗太帅啦!!!
事先说明:为了能够有好的文章体验,建议复制代码尝试效果,或者另寻高见,谢谢
一. 铭记的一个点
首先,我们需要铭记的一个点就是:深拷贝,那就是拷贝内容,开创了新的地址 。所以,你拷贝的那个变量,他指向的地址的值改变,就跟你没有关系,而浅拷贝,则拷贝地址。大家的地址都是一样的,他和你改变了指向的地址的值,那双方都会改变。
这就好像你和你的弟弟,共同拥有一个变形金刚,如果你弄坏了变形金刚,那你的变形金刚坏了,你弟的变形金刚也坏了,这就是浅拷贝对应的情况,如果说你们各有一个变形金刚,你弄坏了,你弟没做啥,那么你的变形金刚坏了,而你弟的好好的(为啥受伤的总是你,因为你没事干嘛要弄坏变形金刚),这就是深拷贝对应的情况。
二. 什么情况下会涉及到深拷贝和浅拷贝
这个点也是需要明确的,就是当你使用到引用型变量的时候,常用的引用型变量就有数组、对象、函数。
引⽤类型:创建⼀个新的指针(指针指向存储堆⾥被复制的对象),将该指针放到为新变量分配的栈空间里
三. 来几个小例子看看你懂了没有
1. 貌似是深拷贝的操作
代码如下(示例):
let dog = {
age:1,
type:'二哈'
}
let dogCopy = dog;
//console.log(dog === dogCopy)//注释的代码建议读者自己尝试哦,===三等号的话会判断地址是否相同
dog = {
age:2,
type:'柯基'
}
// console.log(dog,dogCopy)//{"age":2,"type":"柯基"} {"age":1,"type":"二哈"}
// dog.age = 1;
// dog.type = '二哈';
// console.log(dog)
// console.log(dog === dogCopy)
在这里,dog
的数据改变,但dogCopy
的数据却没有变,难道let dogCopy = dog;
这语句直接就能够实现深拷贝嘛?(运行一下上面注释的代码你就知道了),为了留给读者思考的空间(懒),就不说原因啦!嘿嘿
2. 不是说好是深拷贝嘛?
我们在网上可以搜到很多数组的深拷贝方法,比如这个
var arr = [1,2,3,4,5]
var arr2 = arr.concat()
arr[2] = 5
console.log(arr)
console.log(arr2)
(上面的结果分别是1,2,5,4,5 \n 1,2,3,4,5,修改arr数组的值,arr2数组不变,说明实现深拷贝)
使用concat连接空数组,返回新的数组就是自己,但是它就一定对吗?
我们来看下面的这个代码
let dogArr = [{
age:1,
type:'二哈'
},{
age:2,
type:'二哈'
}]
let dogArrCopy = dogArr.concat()
dogArr[0].type = '柯基';
console.log(dogArr);//[{age: 1, type: '柯基'},{age: 2, type: '二哈'}]浏览器控制台不是这么显示的嘿
console.log(dogArrCopy);//[{age: 1, type: '柯基'},{age: 2, type: '二哈'}]
console.log(dogArr===dogArrCopy);//flase
咦,不是说深拷贝嘛?那怎么dogArr
数组改变的时候dogArrCopy
对应的值也改变了呢?
但是,你有没有发现,dogArr===dogArrCopy
的结果是false呀!这就说明他们确实不是同一个数组呀,那是不是完成了深拷贝呀???
四.案例分析
首先再次明确:深拷贝是行开劈一个空间去拷贝旧的
关于问题1:
这里为啥看似实现了深拷贝(从dog === dogCopy
可以看出,还有就是dog的属性的改变不影响dogCopy
的属性变化)呢,因为
dog = {
age:2,
type:'柯基'
}
这里是给dog指向新的地址,那么修改的值自然和原来那个没有关系,所以这里的话是把旧的值交给dogCopy,然后指向了一个新的空间,这个新的空间的值和旧的空间刚好值类似而已。
关于问题2:
从dogArr===dogArrCopy
的值是false可以知道,这里是实现了深拷贝的,但是,为什么在我们修改dogArr
的属性值的时候,dogArrCopy
也受影响了呢?我们可以看到,从网上找的例子中,他们的数组的值都是基本数据类型,而我们的数组中的值是引用数据类型,我们确实“深拷贝”了数组,即:新开辟了空间,然后让空间的里值等于被复制对象,也就是复制的空间的值是{ age:1, type:'二哈' },{ age:2, type:'二哈' }
那么当我们改变dogArr[0].type = '柯基';
的时候,因为dogArr[0]
和dogArrCopy[0]
指向的是同一个地址,所以修改dogArr[0]
的值时,dogArrCopy[0]
的值就会改变,这里再给问题2补充一下下:
//代码例子二
let dogArr = [{
age:1,
type:'二哈'
},{
age:2,
type:'二哈'
}]
let dogArrCopy = dogArr.concat()
dogArr[0].type = '柯基';
console.log(dogArr);//[{age: 1, type: '柯基'},{age: 2, type: '二哈'}]浏览器控制台不是这么显示的嘿
console.log(dogArrCopy);//[{age: 1, type: '柯基'},{age: 2, type: '二哈'}]
console.log(dogArr===dogArrCopy);//flase
dogArr.push({
age:1,
type:'边牧'
})
console.log(dogArr);// {age: 1, type: '柯基'}{age: 2, type: '二哈'}{age: 1, type: '边牧'}
console.log(dogArrCopy)// {age: 1, type: '柯基'}{age: 2, type: '二哈'}
那这里我们对dogArr做的改变,dogArrCopy就不会跟着变化了
总结
吃饭时间了,总结下次再总结吧,反正没人催更(有人点赞评论,马上总结,说明原因,如果没有,咦,那就等下次想起这篇文章再更新吧,希望这边文章引起你的思考,答案不一定要立刻拿到才是好事对吧)
。。。。。。
好吧,没人催更,那就自己催自己吧:
- 要注意变量的指向性问题,这些在面试题中容易出现,比如原型链那里…
- 在深拷贝上需要注意的是复制的值不能是引用数据类型
比较万能的深拷贝方法(毕竟可能也有不合适的情况):
1 . json字符串转换法:
该方法就是先将对象转换为字符串,再根据json字符串转换成json对象,因为给你的就是字符串,你只能开辟新空间去根据字符串的值去赋值
// 这里的dogArr和上面一样
let dogArrJson = JSON.stringify(dogArr)
let dogArrCopy2 = JSON.parse(dogArrJson);
dogArr[0].age = 100;
console.log(dogArr);// {age: 100, type: '柯基'}{age: 2, type: '二哈'}{age: 1, type: '边牧'}
console.log(dogArrCopy2); {age: 1, type: '柯基'}{age: 2, type: '二哈'}{age: 1, type: '边牧'}
可以看到,这个时候去改变dogArr[0]
不会影响dogArrCopy2
的值了。
2 . 复制到底法,既然我们复制的时候不能够复制到引用数据类型,那当我们复制到引用数据类型的时候继续往下复制直到复制到简单数据类型就好了。
// 深拷贝方法2
function deepCopy(target) {
// 如果不是null值,且不是对象,那就是基本数据类型,那么直接返回结果
if (target == null || !(target instanceof Object)) {
return target;
}
else {
// 如果是对象,那就用该对象的构造函数创建一个新的对象,然后复制其属性值
let copy = new target.__proto__.constructor()
for (let key in target) {
let item = target[key];// 这个是属性值
let deepItemCopy = deepCopy(item);// 属性值进行深拷贝
copy[key] = deepItemCopy;// 深拷贝对象的属性值
}
return copy;
}
}
let arr = [[{
type: 1
}, {
type: 5
}]]
let arrCopy = deepCopy(arr);
arr[0][0].type = 100
console.log(arrCopy);
此处涉及原型和原型链知识,相关知识有兴趣可以查看我的另外一篇文章
《这是你掉的“原型与原型链”吗?快捡起来吧!》(^ o ^)《》
感谢看到这里的你,有用的话请点个赞嘞