浅析JS赋值与深浅拷贝

467 阅读3分钟

浅析JS赋值与深浅拷贝

说到深浅拷贝,那在这就不得不提一下内存和数据类型了。

在JS当中,数据类型分为基本数据类型和引用类型,其中基本数据类型共六种(string,number,boolean,undefined,null,symnol),引用类型为Object(细分可分为Array、object、date......)。

那么在来说一下内存,内存分为栈内存和堆内存,其中栈内存用来存储基本数据类型和引用类型的地址,而堆内存则存储引用数据类型。

一、赋值

什么是赋值呢,其实赋值分为两种,一种是传值,还有一种是传址,顾名思义,前者传的是基本数据类型的值,比如变量的值、字符串或者是undefined,而后者传递的是引用类型的地址。简单来说就是在栈内存中重新开辟一块新的空间,将需要的值复制一份,那有的同学就要说了,这不就是拷贝么?其实不然,对于传值来说,如果将变量a赋值给变量b,复制的仅仅是值,当你改变b的时候,a不受影响,如图:

但对于传址来说,由于复制的是引用类型的地址,那么不管复制多少次,他指向的堆内存中的内存块没有变,也就是说,复制出来的两个地址指向同一个内存块,不管改变哪个对象,他们的值都会一起改变,如图:

二、拷贝

什么是拷贝?拷贝和赋值究竟有何不同?简单来说,二者最根本的不同就在于操作的对象不同,赋值操作的是基本数据类型,而拷贝操作的是引用数据类型,究其本质就是,前者是对栈内存的数据进行的操作,后者是对堆内存的数据进行的操作。

那么现在假设有一个对象如下所示:

var obj={name:"tom",sex:"man",obj1:{age:15}} 

2.1浅拷贝

所谓浅拷贝,也就是只拷贝对象的第一层,即只拷贝最外层的基本数据类型,对于对象中的其他引用类型则只拷贝其地址,也就是说,以上面的obj为例,浅拷贝过来的应该是:

{name:"tom",sex:"man",obj1:{obj1的地址}} 

到此为止,你可以随意更改name、sex,源数据不会受到影响,但当你更改obj1的内容时,源数据中的obj1也会随之发生改变

2.2深拷贝

深拷贝理解起来就非常简单了,就是完完全全的拷贝出一个新的对象,不管源数据有几层,通通拷贝成新的对象,与源数据没有一点关系

三、实现方法

3.1、浅拷贝

<1>通过ES6的object.assign()

var a={name:"tom"}; var b=Object.assign({},a); 

<2>扩展运算符

var a={name:"tom"}; var b={...a}; 

<3>for...in...

let copy=(obj)=>{ 
    let result={};  
    for(var a in obj){ 
        result[a]=obj[a]; 
    }  
    return result; 
} 

3.2、深拷贝

<1>解构赋值

let a={age:10,address:{city:"shanghai"}}; let b={...a,address:{...a.address}} 

<2>递归

function deep(obj){ 
    let result=typeof obj.splice= = ='function'?[]:{};  
        if(obj&&typeof obj = = ="object"){  
            for(let key in obj){   
                if(obj[key]&&typeof obj[key]= = ="object"){  
                    result[key]=deep(obj[key]);  
                }else{  
                    result[key]=obj[key]; 
                } 
            }
      return result;  
    }  
    return obj; 
}