您的位置:首页 > Web前端 > JavaScript

JavaScript 深浅拷贝原理以及内部代码实现过程

2019-01-23 20:20 831 查看

说到深浅拷贝,必须先提到的是JavaScript的数据类型值,分为基本数据类型值和引用数据类型值

基本数据类型指的是简单的数据段:

1.Underfied
2.Null
3.Boolean
4.Number
5.String
6.Symbol (es6新增)

引用类型的值指的是可能包含多个值的对象

为什么要把数据类型这样分呢?本质上,是因为基本数据类型保存在栈内存,引用数据类型保存在堆内存中。那再进一步问:为什么要分两种保存方式呢? 根本原因在于保存在栈内存的必须是大小固定的数据,引用类型的大小不固定,只能保存在堆内存中,但是我们可以把它的地址写在占内存中以供我们访问。贴两个别人文章的图详细说明一下:

图1:

var a = 1;//定义了一个number类型
var obj1 = { name:'Ljf'};//定义了一个引用类型

所以上面这段代码执行后,内存空间是这样:

注:

var a = 1;
var b = a;//复制
console.log(b)//1
a = 2;//改变a的值
console.log(b)//1

所以我们发现复制完b以后,即使改变a的值,b也不会改变,因为a和b是相互独立的,按照上面的图,也就是在栈内存中创建了一个变量b保存的值是1

图2:

var color1 = ['red','green'];
var color2 = color1;//复制
console.log(color2)//['red','green'];
color1.push('black') ;//改变color1的值
console.log(color2)//['red','green','black']

上面这段代码执行后,内存空间是这样:

注:图2结果我们发现只是复制了一次引用类型的地址而已,所以,不管接下来我们是操作color1还是color2,本质上都是操作同一个数组对象。

言归正传,我们现在来讲下深浅拷贝。其实对于基本数据类型的拷贝,并没有深浅拷贝的区别,我们所说的深浅拷贝都是对于引用数据类型而言的

1.浅拷贝:

" = "符号赋值操作

上面的代码是最简单的利用 = 符号赋值操作符实现了一个浅拷贝,可以很清楚的看到,随着 newArray 和 newObject改变,originalArray 和 originalObject也随着发生了变化

“首层浅拷贝”:对目标对象的第一层进行深拷贝,然后后面的是浅拷贝

我们验证了 originalObject=== newObject 是 false,说明这两个对象引用地址不同。originalObject 中关于a没被影响,但是d中的对象被修改了。

结论:从 shallowClone 的代码中我们可以看出,我们只对第一层的目标进行了 深拷贝 ,而第二层开始的目标我们是直接利用 "="符号赋值操作符进行拷贝的,所以,第二层后的目标都只是复制了一个引用,也就是浅拷贝

JavaScript 中Array.prototype.concat() ,Array.prototype.slice(),Object.assign{} … 展开运算符 这些自带的拷贝方法都是都是“首层浅拷贝”

2.深拷贝:

JSON.parse(JSON.stringify())的方法(就是利用JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象)

确实是深拷贝,也很方便。但是这个方法只能适用于一些简单的情况。特殊情况如下:

MDN:
If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array). JSON.stringify can also just return undefined when passing in "pure" values like JSON.stringify(function(){}) or JSON.stringify(undefined).

大概意思就是undefined、function、symbol 会在转换过程中被忽略(还有很多种情况,比如对象中有Error对象,NaN等等都是不同结果)
所以上述例子中对象中含有一个函数,就不能用JSON.parse(JSON.stringify())进行深拷贝。

递归的方法

所以带有函数的对象进行深拷贝用递归就行

结论:JSON.parse(JSON.stringify()) 实现的是深拷贝,但是对目标对象有要求;若想真正意义上的深拷贝,用递归。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: