javascript的深浅拷贝

Posted PeriHe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript的深浅拷贝相关的知识,希望对你有一定的参考价值。

查阅资料,看到有关深浅拷贝上面的误区,有人说数组的slice()与concat()方法实现的是深拷贝。对此我做了一些尝试并详细理了一下关于js的深浅拷贝问题。首先我们要知道数据类型的存储方式——

基本类型和引用类型

js中变量分为两类:

基本类型:undefined,null,字符串,数值,布尔

引用类型:统称为object。具体的有Object,Array,Function等

重点是这两种类型的存储方式了:基本类型的数据是存放在栈内存中的,而引用类型的数据是存放在堆内存中的。

基本数据类型,是这个样子的:

 

引用类型保存在堆中,栈内的是变量的标识符以及对象在堆内存中的存储地址,当需要访问引用类型(如对象,数组等)的值时,首先从栈中获得该对象的地址指针,然后再从对应的堆内存中取得所需的数据。

所以对于这两种类型的赋值是有不同的:

当你在复制基本类型的时候,相当于把值也一并复制给了新的变量。修改值的时候也不会影响另一个变量的值。

而在复制引用类型的时候,实际上只是复制了指向堆内存的地址,即原来的变量与复制的新变量指向了同一个东西。

例1.改变a,会影响b

var a = {name:"peri",age:20};
var b = a;
console.log(a === b);
a.age = 30;
console.log(a);
console.log(b);

深浅拷贝

对于仅仅是复制了引用(地址),即原来的变量和新的变量指向同一个地址,彼此之间的操作会互相影响,为浅拷贝

而如果是在堆中重新分配内存,拥有不同的地址,复制后的对象与原来的对象完全隔离,互不影响,为深拷贝

深浅拷贝的主要区别就是:复制的是引用(地址)还是复制的是实例。

深拷贝的实现

1.递归 

对于例1,我们可以通过递归的方式来实现深拷贝,对引用类型进行遍历,一直到是基本类型为止。

function deepClone(source){    
    if(!source && typeof source !== \'object\'){      
        throw new Error(\'error arguments\', \'shallowClone\');    
    }    
    var targetObj = Array.isArray(source) ? [] : {};    
    for(var keys in source){     
        //只遍历对象自身的属性,而不包含继承于原型链上的属性。  
        if(source.hasOwnProperty(keys)){          
            if(source[keys] && typeof source[keys] === \'object\'){  
                targetObj[keys] = deepClone(source[keys]);    //递归      
            }else{            
                targetObj[keys] = source[keys];         
            }       
        }    
    }    
    return targetObj; 
}
var a = {name:"peri",age:20};
var b = deepClone(a);
console.log(a === b);
a.age = 18;
console.log(a);
console.log(b);

2.JSON 对象的 parse 和 stringify

stringify把一个 js 对象序列化为一个 JSON 字符串。parse把 JSON 字符串反序列化为一个 js 对象,这两个方法实现的是深拷贝。

var obj = {name:\'pp\',age:24,company : { name : \'AAA\', address : \'深圳\'} };
var obj_json = JSON.parse(JSON.stringify(obj));
console.log(obj === obj_json);
obj.company.name = "BBB";
obj.name = "peri";
console.log(obj);
console.log(obj_json);

3.jQuery中的 extend方法

jQuery的$.extend方法是我们在开发中经常用到的方法,用于合并若干个对象。

可用于深浅拷贝,第一个参数为true深拷贝。为false或者没有为浅拷贝。

例2.深拷贝

var obj = {name:\'pp\',age:24,company : { name : \'AAA\', address : \'深圳\'} };
var obj_json = $.extend(true, {}, obj);
console.log(obj === obj_json);
obj.company.name = "BBB";
obj.name = "peri";
console.log(obj);
console.log(obj_json);

例2.2浅拷贝

slice()与concat()方法

最后再来讨论一下最初关于数组的slice()与concat()方法是否深拷贝。

例3.slice()方法

var a = [1,2,3];
var b = a.slice();
console.log(b === a);
a[0] = 4;
console.log(a);
console.log(b);

可以看到,改变a并没有影响b!那么就说明slice()是深拷贝了吗?没有那么简单!

例3.1

var a = [[1,2,3],4,5];
var b = a.slice();
console.log(a === b);
a[0][0] = 6;
console.log(a);
console.log(b);

这很明显说明slice()不是深拷贝!

原来Array 的 slice() 和 concat ()方法,对于第一层的值都是深拷贝而到第二层的时候 Array的slice() 和 concat()方法就是复制引用 。(concat与slice结果相同,这里不再举例)。

 

以上是关于javascript的深浅拷贝的主要内容,如果未能解决你的问题,请参考以下文章

javascript的深浅拷贝

Javascript 对象复制(深浅拷贝)

深浅拷贝

python深浅拷贝

JavaScript深浅拷贝

[记录] JavaScript 中的深浅拷贝(克隆)