深浅克隆(拷贝)

在前端项目开发中,很多情况下都是在操作数据,如果碰到复杂的数据,操作起来就比较困难了。

例如:vue中数据是双向绑定的,页面显示依赖vue实例中的数据驱动,当页面中输入新的数据后,vue实例中的数据也发生了改变。若要将vue实例中的数据当做参数发送请求的时候,接口所需数据跟vue实例中的数据不对应,需要进行修改或删除,那页面中显示的内容也会随着发生变化,为了避免这种情况发生,我们需要对原数据进行复制,对复制出来的数据进行操作,此时就要用到深拷贝和浅拷贝。

深拷贝和浅拷贝主要针对于引用类型数据,因为基本数据类型赋值后,改变新数据,不会影响到原来的数据;而引用数据类型赋值后,改变新数据,将会影响到原来的数据,此时应该使用深拷贝和浅拷贝定义出一个跟原数据一样但互不影响的数据。

注意:赋值操作和深拷贝浅拷贝不是一回事。

赋值

基本类型数据的赋值

基本数据类型包括:numberstringbooleanundefinednull,他们的赋值相对简单,且赋值后两个变量互不影响。

var a = 10;
var b = a;
a = 20;
console.log(a);
console.log(b);

此时的b10,因为将a赋值给b,是将a空间中的值复制了一份,放到了b空间中,改变a,只是在改变a空间,对b空间并没有影响。

基本类型赋值

引用类型数据的赋值

引用数据类型包括:ArrayObject,他们的赋值相对复杂,且赋值后两个变量共享一个数据内存空间,改变其中一个,另一个也会发生改变。

var arr = [1,2,3];
var brr = arr;
arr.push(4);
console.log(arr);
console.log(brr);

此时的brr也有4个元素,跟arr是一模一样的,因为将arr赋值给brr,是将arr中存储的数据空间地址复制了一份,放到了brr中,arrbrr共享同一个数据空间,所以改变其中一个的数据,另一个也会发生改变。

引用数据类型的赋值

深拷贝和浅拷贝的出现,就是为了解决这个问题。

浅拷贝

浅拷贝是将原数据中所有的数据复制一份,放到新的变量空间中,两个变量不共享一个内存地址。

对象浅拷贝

使用系统提供的构造函数Object上的assign方法。

语法:

Object.assign({},原对象)
// 返回新对象

例:

var obj = {
    name:"张三",
    age:12,
    gender:"男"
}
var pbj = Object.assign({},obj);
console.log(obj);
console.log(pbj);

此时的pbjobj是一模一样的

对象浅拷贝

但是objpbj两个变量中存储的数据空间地址是不一样的,例:

obj.name = '李四';
obj.age = 20;
console.log(obj);
console.log(pbj);

此时的pbj一点变化也没有,name键的值还是张三age键的值还是12,因为objpbj不共享数据的内存地址。

拷贝后内存地址不同

注意:如果对象中有数据的值是引用数据类型,在创建新对象的过程中,会将这个引用数据类型的地址也放到新对象中。

var obj = {
    name:"张三",
    age:12,
    gender:"男",
    wife:{
        name:"翠花",
        age:11
    }
}
var pbj = Object.assign({},obj);
obj.wife.gender = "女";
console.log(obj.wife);
console.log(pbj.wife);

此时的pbjwife键对应的对象中,也有了gender键,且值为。当对象的属性的值是引用类型数据的时候,浅拷贝会将这个引用类型数据的地址也拷贝过来,也就是说没有完全的形成一个新对象,还是跟原对象有些关联。这就是浅拷贝。

拷贝中的值是引用类型

数组浅拷贝

Array系统构造函数原型上的concat和slice方法

concat方法
var arr = [1,2,3];
var brr = arr.concat()
arr.push(4);
console.log(arr);
console.log(brr);

此时的brr跟原来的arr是一模一样的,改变了arr后,brr也是没有发生改变的,因为brr的数据空间是一个新的地址。

数组浅拷贝concat

slice方法
var arr = [1,2,3];
var brr = arr.slice();
arr.push(4);
cosnole.log(arr);
cosnole.log(brr);

此时的brr跟原来的arr是一模一样的,改变了arr后,brr也是没有发生改变的,因为brr的数据空间是一个新的地址。

数组浅拷贝-slice

注意:如果数组中的数据有引用类型数据,上面两个方法对于数组的拷贝,会将这个引用类型数据的地址也拷贝出来。

var arr = [1,2,[3,4]];
var brr = arr.concat();
arr[2].push(5);
console.log(arr[2]);
console.log(brr[2]);

此时brr的下标2数据中,也会多出5

数组中有引用类型的浅拷贝

slice也是一样的

var arr = [1,2,[3,4]];
var brr = arr.slice();
arr[2].push(5);
console.log(arr[2]);
console.log(brr[2]);

此时brr的下标2数据中,也会多出5

数组中有引用类型的浅拷贝

这就是浅拷贝,如果数据中都是基本类型数据,就是完全没有联系的两个数据,但是如果数据中引用类型数据,那两个变量还是有一定的联系。

所谓浅拷贝,也就是说,数组或对象中的值如果是基本类型数据,那拷贝后的数据和原数据是完全没有关联,且互不影响的两个数据,如果数组或对象的值是引用类型数据的话,拷贝后的数组或对象中的引用类型的值跟原数据中的引用类型的值,还是存在共享同一地址的现象。

深拷贝

深拷贝,就是不管原数据中值是什么类型的数据,拷贝后的新数据跟原数据是完全没有关联的。

1、利用json数据和json字符串之间的转换

对象深拷贝

var obj = {
    name:"张三",
    age:12,
    wife:{
        name:"翠花",
        age:20
    }
}
var str = JSON.stringify(obj);
var pbj = JSON.parse(str);
obj.wife.gender = '女';
console.log(obj.wife);
console.log(pbj.wife);

此时pbjwife数据中没有gender键,不受obj的影响。

对象深拷贝

数组深拷贝

var arr = [1,2,[3,4]];
var str = JSON.stringify(arr);
var brr = JSON.parse(str);
arr[2].push(5);
console.log(arr[2]);
console.log(brr[2]);

此时brr下标2的数据没有5,不受arr的影响。

数组深拷贝

2、利用递归遍历对象或数组

function clone(source){
    var result;
    if(Object.prototype.toString.call(source)==='[object Object]'){
        result = {};
    }else if(Object.prototype.toString.call(source)==='[object Array]'){
        result = []
    }else{
        return;
    }
    for(var attr in source){
        if(Object.prototype.toString.call(source[attr])==='[object Array]' || Object.prototype.toString.call(source[attr])==='[object Object]'){
            result[attr] = clone(source[attr])
        }else{
            result[attr] = source[attr];
        }
    }
    return result;
}

使用:

var arr = [1,2,{
    name:"张三",
    age:12,
    wife:{
        name:"翠花",
        age:11
    }
}];
var res = clone(arr)

res[0] = 5;
console.log(arr[0],res[0]); // 1 5
res[2].name = '李四'
console.log(arr[2].name,res[2].name); // 张三 李四

调用函数得到的新数据和原来的数据互不影响。


递归深拷贝

总之,赋值是赋值,拷贝是拷贝。

浅拷贝,当对象或数组中的数据都是基本数据类型的时候,两个数据之间完全是独立的,如果对象或数组中的值是引用类型的时候,里面是引用类型的值,还是会保持共同的内存地址;

深拷贝出来的两个数据是完全独立的。

你可能感兴趣的:(深浅克隆(拷贝))