浅拷贝
在介绍浅拷贝之前,先了解一下,数据类型的分类,我们知道共用六大类,其中有5中基本数据类型和一种引用类型
五大基本数据类型:
1.Number
2.String
3.Boolean
4.undefined
5.null
引用类型(Object)
1.object
2.array
3.function
注意:基本数据类型式存储在栈当中,而引用类型存储在堆当中。
1.基本数据类型的数据存储在栈中,就好比我们使用var 声明变量是开辟了一块儿空间,把数值存储在里面,用的时候取到的也是里面的值。
2.引用类型存放在堆中,存放在堆中的数据有比较明显的特征就是它需要占用的内存不确定可能大,也可能小。在使用的时候,是去找到其在堆中所对应得地址。
了解到以上的知识我们再来看看拷贝,拷贝顾名思义就是将一份数据拷贝(复制一份到)到另一个存储空间中
浅拷贝
我先举一个例子:比如我这边有一个数组var arr1 = [1,2,3,4,5,6],然后再创建一个空的数据var arr2 = [],我把arr1中的数据拷贝一份给到arr2中,请先看下面一段代码:
var arr1 = [1,2,3,4,5];
var arr2 = arr1;
console.log(arr2); //[1,2,3,4,5];
arr1.push(6); //向arr1添加一个数据6
console.log(arr2);//[1,2,3,4,5,6]
其实前面提到了数组属于特殊数据类型,我们赋值给arr2,只是将它的地址赋值给了arr2,所以arr2的地址指向arr1中的地址,所以我们向arr1中添加数据时,由于arr2和arr1使用的是同一块儿地址所以出现上面的情况
再看一下接下来的代码:
var arr1 = [1,2,3,4,5];
var arr2 = [];
for(i = 0;i<arr1.length;i++)
{
arr2[i] = arr1[i];
}
console.log(arr2);//[1,2,3,4,5]
arr1.push(6);
console.log(arr2);//[1,2,3,4,5]
可以看到重新声明一个变量arr2,此时arr开辟了一块儿存储空间我们通过循环将arr1中的值付给arr2,再向arr1中添加数据也不会影响arr2的数据,因为此时arr2是另一块儿存储空间。这就是一个简单的浅拷贝,其实这样拷贝出来的数据还存在一种问题,仔细看下面这段代码:
var arr1 = [1,2,3,4,5,[1,2]];
var arr2 = [];
for(i = 0;i<arr1.length;i++)
{
arr2[i] = arr1[i];
}
arr1[5].push(3);
console.log(arr2);//[1,2,3,4,5,[1,2,3]]
为什么通过for循环将数据付给arr2,还会修改数据,这是因为在拷贝过程中,最后一组数据还是一个数据所以拷贝的时候还是把地址给到arr2,所以相当于arr2中的最后一条数据和arr1的最后一条是共用一块儿存储空间
刚刚说了,如果拷贝的数据中还是引用类型的数据,就是会出现共用同一块儿存储空间的情况,所以呢?对象也存在这样的情况,下面简单介绍一下对象出现的问题:请看下面下一段代码:
var obj1 =
{
"name":"枫叶在飘呀",
"age":21,
"other":
{
"hobby":"打篮球"
}
}
var obj2 = {}
//把obj1的数据给到obj2中,对象循环一般使用forin
for(var key in obj1)
{
obj2[key] = obj1[key]
}
obj1.other.hobby = "打羽毛球"
console.log(obj2);
// {name: "枫叶在飘呀", age: 21, other: {…}}
// age: 21
// name: "枫叶在飘呀"
// other: {hobby: "打羽毛球"}
obj2中other对象中的hobby属性被修改了.
那如何实现将数据完全拷贝过去,并不受原来数据的影响呢?,这时候就是应该使用数据的深拷贝了。
深拷贝
在了解深拷贝同时了解一下,如何确定引用数据类型的具体数据类型,可能第一时间想到的是用typeof或者是instanceof,但是typeof 检测引用类型都是object类型的,我们知道引用数据类型有一些系统自带的方法,其中object类型在使用toString()方法的时候,会有一个隐式检测类型,我们一起来看一下
var arr1 = [];
console.log(arr1.toString()); //空
console.log({}.toString())//[object Object]
那数组没有这种隐式检测类型想使用一下怎么办?
var arr1 = [];
console.log(Object.prototype.toString.call(arr1)); //[object Array]
console.log({}.toString())//[object Object]
在调用对象原型中的toString方法时改变一下this的指向让它指向数组,那数组也就可以使用了。所以我想封装一个函数让他深度拷贝数组和对象两种类型的数据
请看代码:
var obj1 =
{
"name":"枫叶在飘呀",
"age":21,
"other":
{
"hobby":"打篮球"
}
}
function getType(data)
{
return Object.prototype.toString.call(data).slice(8,-1) //[Object Array] (8)表示从第八个数开始,-1表示倒数第一位
}
function toCopy(data)
{
if(getType(data) == "Array")
{
var reg = []; //检测是Array,就创建一个数组用来接收一下
}
else if(getType(data) == "Object")
{
var reg = {}; //检测的是Object,就用对象接受一下
}
else //如果是其他数据类型,他们不存在这样的问题,所以不需要
{
return data
}
for(var key in data)//数组和对象都可以使用
{
if(getType(data[key]) == "Array" || getType(data[key]) == "Object")
{
//递归
reg[key] = toCopy(data[key])
}
else
{
reg[key] = data[key];
}
}
return reg;
}
var arr1 = [1,2,3,4,[1,2]]
var arr2 = toCopy(arr1);
console.log(getType(arr1));
arr1[4].push(3)
console.log(arr2);//[1,2,3,4,[1,2]]
这样就完成了深拷贝。