浅拷贝与深拷贝

192 阅读3分钟

一、数据类型:

基本数据类型:string、number、boolean、null、undefined

引用数据类型:Object、Array、Function

基本数据类型的特点:直接把值存储在栈中的数据;

引用数据类型的特点:栈中存储的是引用(地址),真正的对象是保存在堆内存里面的。

二、深拷贝和浅拷贝:

深拷贝和浅拷贝主要是针对引用数据类型。

如图所示:

浅拷贝:


深拷贝:


浅拷贝只复制指向某个对象的指针,而不复制对象本身,两个变量指向同一块内存空间。但是深拷贝会另外创建一个一模一样的对象,新旧对象不共享内存,对其中一个对象的修改不会影响另一个对象。

三、浅拷贝的实现方式:

1、直接把用用数据赋值给一个变量

let obj = {
    name:'kimi',
    age:'26'
};
let obj1 = obj;
obj.name = 'haha';
console.log(obj);
console.log(obj1);


2、Object.assign()

let obj = {
    name:'kimi',
    age:'26'
};
let obj1 = Object.assign(obj);
obj.name = 'hehe';
console.log(obj);
console.log(obj1);


3、Array.prototype.concat()

let arr = [1,2,{
    name:'kimi'
}];
let arr1 = arr.concat();
arr1[2].name = 'jinjin';
console.log(arr);
console.log(arr1);


4、Array.prototype.slice()

let arr = [1,2,{
    name:'kimi'
}];
let arr1 = arr.slice();
arr1[2].name = 'mimi';
console.log(arr);
console.log(arr1);


深入理解数组的slice()和concat()方法

Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。

原数组的元素会按照下述规则拷贝:

  • 如果该元素是个对象引用(不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
  • 对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。

例:

let arr = [1,2,{
    name:'kimi'
}];
let arr1 = arr.slice();
arr1[1] = 3;
console.log(arr);
console.log(arr1);


原数组arr中基本数据类型不受影响。

四、深拷贝的实现

1、JSON.parse(JSON.stringify())

let arr = [1,2,{
    name:'kimi'
}];
let arr1 = JSON.parse(JSON.stringify(arr));
arr1[2].name = "jinjin";
console.log(arr);
console.log(arr1);


JSON.stringify()将对象转成字符串,此时进行的是值的复制,然后再将字符串准换成对象,这样产生的新对象,开辟了新的内存空间,实现了深拷贝。

存在问题:

(1)这种方法虽然可以实现数组或对象深拷贝,但不能处理函数;

例:

let arr = [1,2,{
    name:'kimi'
},function(){}];
let arr1 = JSON.parse(JSON.stringify(arr));
arr1[2].name = "jinjin";
console.log(arr);
console.log(arr1);


这是因为JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,不能接受函数。

2、递归实现深拷贝

function deepCopy(obj){
    var res = Array.isArray(obj)?[]:{};
    if(obj && typeof obj === 'object'){ 
        for(var key in obj){
            if(obj.hasOwnProperty(key)){
                if(obj[key] && typeof obj[key] === 'object'){
                    res[key] = deepCopy(obj[key]);   //注意要有返回值
                }else{
                    res[key] = obj[key];
                }
            }
        }
    }
    return res;
}
var obj = {
    name:'kimi',
    age:26,
    friend:['jinjin'],
    sayName:function(){
        console.log(this.name);
    }
};
var res = deepCopy(obj);
console.log(obj);
console.log(res);


note:循环引用

var obj = {
    a:1,
    b:obj
};
var res = deepCopy(obj);
console.log(obj);
console.log(res);

3、函数库lodash

该函数库也有提供_.cloneDeep用来做 Deep Copy


该方法可以解决循环引用、存在函数、和undefined这三种情况的深拷贝。