堆與棧是什么晋控,與淺拷貝和深拷貝有什么聯(lián)系?
在計算機領(lǐng)域姓赤,堆棧是兩種數(shù)據(jù)結(jié)構(gòu)赡译,都是一種數(shù)據(jù)項按序排列的數(shù)據(jù)結(jié)構(gòu),只能在一端(稱為棧頂(top))對數(shù)據(jù)項進行插入和刪除不铆。
深拷貝和淺拷貝的主要區(qū)別就是其在內(nèi)存中的存儲類型不同蝌焚,而堆和棧都是內(nèi)存中劃分出來用于存儲的區(qū)域。
棧(stack)為系統(tǒng)自動分配的內(nèi)存空間誓斥,它由系統(tǒng)自動釋放只洒;而堆(heap)則是動態(tài)分配的內(nèi)存,大小不定也不會自動釋放劳坑。
ECMAScript 的數(shù)據(jù)類型
因此在了解淺拷貝與深拷貝之前毕谴,我們先來了解一下ECMAScript 中的數(shù)據(jù)類型。
基本類型
基本類型主要有:undefined距芬,boolean涝开,number,string框仔,null
基本類型是存放在棧內(nèi)存中的簡單數(shù)據(jù)段舀武,數(shù)據(jù)大小確定,內(nèi)存空間大小可以分配离斩,直接按值存放的银舱,所以可以直接訪問瘪匿。
引用類型
引用類型主要有:object,array寻馏,function
引用類型是存放在堆內(nèi)存中的對象棋弥。變量實際在棧內(nèi)存中保存了一個指針,這個指針指向堆中的一個空間操软。每個空間大小不一樣嘁锯,要根據(jù)情況來進行特定的分配。當我們需要訪問引用類型的值時聂薪,首先從棧中獲得該對象的地址指針家乘,然后再從堆內(nèi)存中取得所需的數(shù)據(jù)。
基本類型與引用類型的主要區(qū)別
基本類型與引用類型最大的區(qū)別實際就是傳值與傳址的區(qū)別 藏澳。
var a = [1,2,3,4,5];
var b = a; //傳址
var c = a[0]; //傳值
console.log(b); //1,2,3,4,5
console.log(c); //1
b[4] = 6; //改變b數(shù)組的某一項值
c = 7; //改變c的值
console.log(a[4]); //6
console.log(a[0]); //1
從上面我們可以得知仁锯,當我改變b數(shù)組時,a數(shù)組也發(fā)生了變化翔悠。但是當我改變c的值時业崖,a卻沒有發(fā)生改變。這就是傳值與傳址的區(qū)別蓄愁。
因為a是數(shù)組双炕,屬于引用類型,所以它賦予給b的時候傳的是棧中的地址(相當于新建了一個不同名“指針”)撮抓,而不是堆內(nèi)存中的對象妇斤,所以a和b是指向同一個堆內(nèi)存。而c僅僅是從a堆內(nèi)存中獲取的一個數(shù)據(jù)值丹拯,并保存在棧中站超。所以當b修改的時候,會根據(jù)地址回到a堆內(nèi)存中修改乖酬,而c則直接在棧中修改死相,并且不能指向a堆內(nèi)存中。
淺拷貝
有上面的內(nèi)容可以知道咬像,在定義一個對象或數(shù)組時算撮,變量存放的往往只是一個堆地址。當我們使用對象拷貝時县昂,如果屬性是對象或數(shù)組時肮柜,這時候我們傳遞的也只是一個堆地址。因此子對象在訪問該屬性時七芭,會根據(jù)地址回溯到父對象指向的堆內(nèi)存中素挽,即父子對象發(fā)生了關(guān)聯(lián) 蔑赘,兩者的屬性值會指向同一內(nèi)存空間狸驳。
例子:
var a = {
key1: 123,
key2: [1,2]
};
function copy(o){
var obj = {};
for(var i in o){
obj[i] = o[i];
}
return obj;
}
var b = copy(a);
b.key1 = 1234; //b對象的key1重新賦值
b.key2.push(3); //b對象的key2數(shù)組里面插入一個新的值
console.log(b.key1); //1234
console.log(a.key1): //123
console.log(b.key2); //1,2,3
console.log(a.key2); //1,2,3
上述代碼中预明,可以看出b的key1值改變了但是a的key1的值沒有隨著發(fā)生改變,但b的key2值改變后卻導(dǎo)致a的key2的值也發(fā)生了改變耙箍。
這是因為key1的值屬于基本類型撰糠,所以拷貝的時候傳遞的就是該數(shù)據(jù)段。但是key2的值是堆內(nèi)存中的對象辩昆,所以key2在拷貝的時候傳遞的是指向key2對象的地址阅酪,無論復(fù)制多少個key2,其值始終是指向a的key2對象的內(nèi)存空間汁针。
深拷貝
有時候术辐,我們不希望父子對象之間產(chǎn)生關(guān)聯(lián),那么這時候可以用到深拷貝施无。既然屬性值類型是數(shù)組和或象時只會傳址辉词,那么我們就用以下的方法來解決這個問題,把父對象中所有屬于對象的屬性類型都遍歷賦給子對象即可猾骡。
-
JSON方法
function deepCopy(o){ return JSON.parse(JSON.stringify(o)); } var a = { key: [1,2] }; var b = deepCopy(a); //拷貝a b.key.push(3); //往b的key中插入新的值 console.log(a); //1,2 console.log(b); //1,2,3
-
利用遞歸
function deepCopy(o,c){ var c = c || {}; for(var i in o){ if(typeof o[i] === 'object'){ c[i] = (o[i].constructor === Array) ? [] : {}; copy(o[i], c[i]); }else{ c[i] = o[i]; } } return c; } var a = { key: [1,2] }; var b = deepCopy(a); //拷貝a b.key.push(3); //往b的key中插入新的值 console.log(a); //1,2 console.log(b); //1,2,3