欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

深拷贝与浅拷贝

程序员文章站 2022-05-28 14:30:04
...

深拷贝和浅拷贝主要是针对引用类型的值的赋值(或复制)操作。

一、区分基本类型和引用类型

基本类型的值 引用类型的值
Undefined,Null,Boolean,String,Number Object,Array,Date,RegExp,Function
按值访问(可以操作保存在变量中的实际的值) 按引用访问(操作对象时,实际上是操作对象的引用)
保存在变量中的实际的值 保存在内存中的对象(不允许直接访问)
赋值时,前后两个变量时独立的 赋值时,前后两个变量指向内存中的同一位置(引用类型的实际值存放在堆中)
对于向函数传参,都是按值传递(但是引用类型的传参有点特别,如果不是重写,那么参数和外面的变量都是指向同一个堆位置,如果重写了,那参数就指向一个新创建的局部变量,原对象无影响,这说明不是按引用传递)

检测类型的方法

1、使用typeof,可区分出string,number,boolean,undefined,object,function等

2、使用instanceof :obj instanceof Object,如果是引用类型则返回 true,否则返回false。

二、基本类型的赋值(或复制)

例一:

var num1 = 1;
var num2 = num1;  //num2 = 1
num2 = 2;  //num2 = 2,num1 = 1

复制前的变量对象

 

 

 

 

Num1

1(Number类型)

复制后的变量对象

 

 

Num2

1(Number类型)

Num1

1(Number类型)

基本类型的赋值操作后的两个变量,是独立的,改变num2的值,不会影响到num1。

三、引用类型的赋值(或复制)

例二:

var obj1 = {
    name : 'zsx',
    age : 10
}
var obj2 = obj1;  //obj1 = {name: "zsx", age: 10},obj2 = {name: "zsx", age: 10}
obj2.name = 'changeName';  //obj1 = {name: "changeName", age: 10},obj2 = {name: "changeName", age: 10}

复制前的变量对象

 

 

 

 

Obj1

堆地址1:指向存储在堆中的一个对象(Object类型)

复制后的变量对象

 

 

Obj2

堆地址1:指向存储在堆中的一个对象(Object类型)

Obj1

堆地址1:指向存储在堆中的一个对象(Object类型)

堆内存

深拷贝与浅拷贝

引用类型的赋值操作,其实复制的是指针,相同的指针使得前后两个变量指向的对象一致,因此改变obj2,会反映到obj1。

例三:

var arr1 = [1,2,3];
var arr2 = arr1;  //arr1 = [1, 2, 3],arr2 = [1, 2, 3]
arr2[0] = 0;  //arr1 = [0, 2, 3],arr2 = [0, 2, 3]

例四:

var array=[];
var item={};
for(var i=0;i<5;i++){    
    item.a=i;   //在这里,修改的都是堆中同一个位置的对象,因此array中的元素都指向同一个item   
    array.push(item);   
}
console.log(array);  // [ { a: 4 }, { a: 4 }, { a: 4 }, { a: 4 }, { a: 4 } ]

for(var i=0;i<5;i++){    
    var item={};   //在这里,每次都新建一个对象,array中的元素指向五个不同的item 
    item.a=i;    
    array.push(item);   
} //[ { a: 0 }, { a: 1 }, { a: 2 }, { a: 3 }, { a: 4 } ]

四、引用类型的浅拷贝

//浅拷贝实现
function shallowCopy (obj) {
    var newObj = {};
    for(let key in obj) {
        if(obj.hasOwnProperty(key)) {
            newObj[key] = obj[key]; //复制obj拥有的直接属性
        }
    }
    return newObj;
}

例五:

var arr1 = [1,{a:2},[3,4]];
var arr2 = arr1.slice(0);  //arr1 =  [1,{a:2},[3,4]],arr2 = [1,{a:2},[3,4]]
arr2[0] = 0;  //arr1 =  [1,{a:2},[3,4]],arr2 = [0, {a:2}, Array(2)],浅拷贝
arr2[1].a = 22;  //arr1 =  [1,{a:22},[3,4]],arr2 = [1,{a:22},[3,4]],元素中的对象是赋值操作
arr2[2][0] = 33;  //arr1 =  [1,{a:2},[33,4]],arr2 = [1,{a:2},[33,4]] ,元素中的数组是赋值操作

例六:

var arr1 = [1,{a:2},[3,4]];
var arr2 = arr1.concat();  //arr1 =  [1,{a:2},[3,4]],arr2 = [1,{a:2},[3,4]]
arr2[0] = 0;  //arr1 =  [1,{a:2},[3,4]],arr2 = [0, {a:2}, Array(2)],浅拷贝
arr2[1].a = 22;  //arr1 =  [1,{a:22},[3,4]],arr2 = [1,{a:22},[3,4]],元素中的对象是赋值操作
arr2[2][0] = 33;  //arr1 =  [1,{a:2},[33,4]],arr2 = [1,{a:2},[33,4]] ,元素中的数组是赋值操作

例七:

var obj1 = {
    name : 'zsx',
    friend : {
        name : 'xiaohong',
        age : 12
    }
}
var obj2 = Object.assign({}, obj1);  //obj1 = {name: "zsx", friend: {name: "zsx", age: 12}},obj2 = {name: "zsx", friend: {name: "zsx", age: 12}}
obj2.friend.name = 'changeName';  //obj1 = {name: "zsx", friend: {name: "changeName", age: 12}},obj2 = {name: "zsx", friend: {name: "changeName", age: 12}},浅拷贝 

无论是利用slice、concat和Object.assign(),都是浅拷贝,子对象(子数组)是赋值操作。

五、引用类型的深拷贝

例八:利用递归进行深拷贝

//简单版深拷贝函数
function deepCopy (obj) {
    //如果需要拷贝的obj是null,不是数组或者对象,则直接返回obj
    if (obj === null || typeof obj !== 'object') {
        return obj
    }
    var copy = Array.isArray(obj) ? [] : {};  //判断是对象还是数组

    //通过Object.keys(obj)的方法,从obj中获取一个key数组
    //注意:Object.keys(obj)不遍历原型链
    Object.keys(obj).forEach(key => {
        copy[key] = deepCopy(obj[key]);
    });

    return copy;
}
var obj1 = {
    name : 'zsx',
    friend : {
        name : 'xiaohong',
        age : 12
    }
}
var obj2 = deepCopy(obj1);  //obj1 = {name: "zsx", friend: {name: "xiaohong", age: 12}},obj2 = {name: "zsx", friend: {name: "xiaohong", age: 12}}
obj2.friend.name = 'changeName';  //obj1 = {name: "zsx", friend: {name: "xiaohong", age: 12}},obj2 = {name: "zsx", friend: {name: "changeName", age: 12}}

例九:利用JSON.stringify()和JSON.parse()进行深拷贝

var obj1 = {
    name : 'zsx',
    friend : {
        name : 'xiaohong',
        age : 12
    }
}
//利用JSON.stringify()方法将obj1转换为字符串,将字符串赋值给obj2
var obj2 = JSON.stringify(obj1); 
//再利用JSON.parse()方法解析json,重新分配空间给obj2
obj2 = JSON.parse(obj2);
obj2.friend.name = 'changeName';  //obj1 = {name: "zsx", friend: {name: "xiaohong", age: 12}},obj2 = {name: "zsx", friend: {name: "changeName", age: 12}}

六、区分赋值(=)、浅拷贝和深拷贝

赋值(=):只赋值引用类型整体的地址。

浅拷贝:复制对象(数组)的直接元素,子对象(子数组)进行赋值操作。

深拷贝:复制对象(数组)中的属性(元素),包括子对象(子数组)。