JavaScript的賦值、深拷貝和淺拷貝

JavaScript的內存空間

在JavaScript中,每個數據都須要一個內存空間。內存空間分爲兩種,棧內存(stack)與堆內存(heap)es6

棧是系統自動分配的內存空間,由系統自動釋放,堆則是動態分配的內存,大小不定不會自動釋放。數組

基本數據類型

JavaScript中的基本數據類型,這些值都有固定的大小,保存在內存中,由系統自動分配存儲空間在棧內存空間的值,咱們能夠直接進行操做,所以基礎數據類型都是按照值訪問函數

在棧內存中的數據發生複製的行爲時,系統會自動爲新變量開闢一個新的內存空間,當複製執行後,兩個內存空間的值就互不影響,改變其中一個不會影響另外一個this

棧內存空間數據複製示例
var a = `I am variable a`;
var b = a; 
console.log(b); //`I am variable a`
b = `I am variable b`;
console.log(a); //`I am variable a`
console.log(b); //`I am variable b`

引用數據類型

引用類型的值是保存在內存中的對象,在JavaScript中咱們不能直接操做對象的堆內存空間。由於引用類型的值都是按引用訪問的,因此在操做對象時,其實是操做對象的引用而不是實際的對象。引用能夠理解爲保存在棧內存中的一個地址,該地址指向堆內存中的一個實際對象指針

引用類型值的複製,系統會爲新的變量自動分配一個新的棧內存空間這個棧內存空間保存着與被複制變量相同的指針,儘管他們在棧內存中的內存空間的位置互相獨立可是在堆內存中訪問到的對象其實是同一個,所以,當咱們改變其中一個對象的值時,實際上就是改變原來的對象code

棧內存空間保存指針(地址),堆內存空間保存實際的對象,咱們經過變量訪問對象時,實際上訪問的是對象的引用(地址)對象

內存中的棧區域存放變量(基本類型的變量包括變量聲明和值)以及指向堆區域存儲位置的指針(引用類型的變量包括變量聲明和指向內容的指針)遞歸

var a = {
    name : `I am object a`,
    type : 'object'
}

var b = a;
console.log(b);
// {name: "I am object a", type: "object"}

b.name = `I am object b`;

console.log(a);
// {name: "I am object b", type: "object"}

console.log(b);

// {name: "I am object b", type: "object"}

基本類型總結

基本數據類型ip

包括:null、undefined、number、string、boolean、symbol(es6)內存

存放位置:內存中的棧區域中

比較:值的比較,判斷是否相等,若是值相等,就相等。通常使用===進行比較,由於==會進行類型的轉換

拷貝:賦值(經過(=)賦值操做符 賦值),賦值完成後,兩個變量之間就沒有任何關係了,改變其中一個變量的值對另外一個沒有任何影響

引用類型總結

引用數據類型

包括:數組、對象、函數

存放位置:內存的棧區域中存放變量和指針,堆區域存儲實際的對象

比較:是引用的比較(就是地址的比較,變量在棧內存中對應的指針地址相等就指向同一個對象)判斷是否爲同一個對象,示例以下

變量a和變量b的引用不一樣,對象就不是同一個對象
var a = {name:'Jay'};
var b = {name:'Jay'};
a===b //false

咱們對JavaScript中引用類型進行操做的時候,都是操做其對象的引用(保存在棧內存中的指針)

賦值、深拷貝和淺拷貝 (Assignment, deep copy and shallow copy)

賦值:兩個變量的值(指針)都指向同一個對象,改變其中一個,另外一個也會受到影響

所謂拷貝就是複製,經過複製原對象生成一個新的對象

淺拷貝:從新在堆內存中開闢一個空間,拷貝後新對象得到一個獨立的基本數據類型數據,和原對象共用一個原對象內的引用類型數據,改變基本類型數據,兩個對象互不影響,改變其中一個對象內的引用類型數據,另外一個對象會受到影響

var obj = {
    name: 'Jay Chou',
    age: 32,
    song:{
        name:'發如雪',
        year:2007
    }
}
var obj1 = obj;
function shallowCopy(obj){
    var scObj = {};
    for(var prop in obj){
        if(obj.hasOwnProperty(prop)){
            scObj[prop] = obj[prop]
        }
    }
    return scObj;
}
var obj2 = shallowCopy(obj);
console.log(obj === obj1,'obj === obj1','賦值');
console.log(obj === obj2,'obj === obj2','淺拷貝');
// true "obj === obj1" "賦值"
// false "obj === obj2" "淺拷貝"
console.log(obj.song === obj2.song);
//true
obj2.song.name='雙截棍';
obj2.name='Jay';
console.log(obj)
// {name: "Jay Chou", age: 32, song: {name:'雙截棍',year:2007}}
console.log(obj1);
// {name: "Jay Chou", age: 32, song: {name:'雙截棍',year:2007}}
console.log(obj2);
{name: "Jay", age: 32, song: {name:'雙截棍',year:2007}}
console.log(obj===obj1)
//true
console.log(obj===obj2)
//false

深拷貝:不管是對象內的基本類型仍是引用類型都被徹底拷貝,拷貝後兩個對象互不影響

一種比較簡單實現方法是使用var dcObj = JSON.parse(JSON.stringify(obj))

var obj = {
    name: 'Jay Chou',
    age: 32,
    song:{
        name:'發如雪',
        year:2007
    }
}

var dcObj=JSON.parse(JSON.stringify(obj));

console.log(dcObj);
// {name: "Jay Chou", age: 32, song: {name:'發如雪',year:2007}}
console.log(dcObj.song === obj.song);
//false
dcObj.name='Jay';
dcObj.song.name='雙截棍';
console.log(obj);
// {name: "Jay Chou", age: 32, song: {name:'發如雪',year:2007}}
console.log(dcObj);
//{name: "Jay", age: 32, song: {name:'雙截棍',year:2007}}

須要注意的是,使用JSON.Stringify()序列化對象時會把對象內的function和原型成員忽略掉,示例以下

var obj = {
    name: 'Jay Chou',
    job: 'artist',
    say:function(){
        alert(this.job);
    }
}
JSON.stringify(obj);
//"{"name":"Jay Chou","job":"artist"}"

經過遞歸淺拷貝函數實現深拷貝

function deepCopy(obj){
    if(!obj || typeof obj !== 'object'){
        return ;
    }
    var dcObj = Array.isArray(obj) ? [] : {};
    for(var key in obj){
        if(obj.hasOwnProperty(key)){
            if(obj[key] && typeof obj[key] === 'object'){
                dcObj[key] = Array.isArray(obj[key]) ? [] : {};
                dcObj[key] = deepCopy(obj[key]);
            }
            dcObj[key] = obj[key]
        }
    }
    return dcObj;
}

比較:賦值、深拷貝、淺拷貝

賦值:變量得到原對象的引用,改變該引用指向的對象的值(基本類型引用類型)其實就是修改原對象的值

淺拷貝:改變新對象基本類型的值不會使原對象對應的值一塊兒改變,可是改變新對象引用類型的值使原對象對應的值一同改變

深拷貝:改變新對象基本類型引用類型的值,都不會影響原對象,二者互相獨立,互不影響

相關文章
相關標籤/搜索