一.棧和堆
棧(stack):棧會(huì)自動(dòng)分配內(nèi)存空間,會(huì)自動(dòng)釋放,存放基本類型,簡(jiǎn)單的數(shù)據(jù)段,占據(jù)固定大小的空間。
基本類型:String,Number,Boolean,Null,Undefined
堆(heap):動(dòng)態(tài)分配的內(nèi)存,大小不定也不會(huì)自動(dòng)釋放,存放引用類型,指那些可能由多個(gè)值構(gòu)成的對(duì)象,保存在堆內(nèi)存中,包含引用類型的變量,實(shí)際上保存的不是變量本身,而是指向該對(duì)象的指針。
引用類型:Function,Array,Object
二.區(qū)別
棧:所有在方法中定義的變量都是放在棧內(nèi)存中,隨著方法的執(zhí)行結(jié)束,這個(gè)方法的內(nèi)存棧也自然銷毀。
優(yōu)點(diǎn):存取速度比堆快,僅次于直接位于CPU中的寄存器,數(shù)據(jù)可以共享;
缺點(diǎn):存在棧中的數(shù)據(jù)大小與生存期必須是確定的,缺乏靈活性。
堆:堆內(nèi)存中的對(duì)象不會(huì)隨方法的結(jié)束而銷毀,即使方法結(jié)束后,這個(gè)對(duì)象還可能被另一個(gè)引用變量所引用(參數(shù)傳遞)。創(chuàng)建對(duì)象是為了反復(fù)利用,這個(gè)對(duì)象將被保存到運(yùn)行時(shí)數(shù)據(jù)區(qū)。
三.棧和堆的溢出
棧:可以遞歸調(diào)用方法,這樣隨著棧深度的增加,JVM維持著一條長(zhǎng)長(zhǎng)的方法調(diào)用軌跡,知道內(nèi)存不夠分配,產(chǎn)生棧溢出。
堆:循環(huán)創(chuàng)建對(duì)象,通俗點(diǎn)就是不斷的new 一個(gè)對(duì)象。
下面來(lái)看看傳值和傳址的區(qū)別
其實(shí)這兩者區(qū)別就是基本類型和引用類型的區(qū)別,話不多說(shuō)看栗子
var a = [1,0,9,8,7];
var b = a;
var c = a[0];
console.log(b); //[1,0,9,8,7]
console.log(c); //1
//改變數(shù)值
b[1] = 3;
c = 5;
console.log(b[1]); //3
console.log(a[0]); //1
因?yàn)閍是數(shù)組,是引用類型,賦給b的時(shí)候傳的是棧中的地址,不是堆內(nèi)存中的對(duì)象,c僅僅是從a堆內(nèi)存中獲取的一個(gè)數(shù)據(jù)值,并保存在棧中,所以b修改的時(shí)候,會(huì)根據(jù)地址回到a堆中修改,c則直接在棧中修改,并且不能指向a堆內(nèi)存中。
四.深淺拷貝
深淺拷貝在前端面試中經(jīng)常被問(wèn)到,和大家分享一下,先來(lái)說(shuō)說(shuō)淺拷貝
淺拷貝:也就是只復(fù)制了第一層屬性,復(fù)制對(duì)象是基本類型
在復(fù)制基本類型時(shí),直接使用等號(hào)完成,在復(fù)制引用類型時(shí),循環(huán)遍歷對(duì)象,對(duì)每個(gè)屬性或值使用等號(hào)完成。
下面看個(gè)栗子
var color1 = ['red','green'];
var color2 = [];
//復(fù)制
for(var i = 0;i < color1.length;i++){
color2[i] = color1[i];
}
console.log(color2); //[red,green]
color1.push('black');
console.log(color2); //[red,green]
在這個(gè)栗子中,color2復(fù)制color1,因?yàn)閿?shù)組中的每一項(xiàng)都是基本類型(string),假如數(shù)組中的某一項(xiàng)保存的是一個(gè)對(duì)象,或者是一個(gè)數(shù)組,又或者說(shuō)對(duì)象的某一個(gè)屬性還是一個(gè)對(duì)象(也就是引用類型的某個(gè)屬性還是引用類型),此時(shí)淺拷貝就沒(méi)用了,那該怎么辦?
我們先來(lái)看一個(gè)引用類型屬性還是引用類型的栗子(有點(diǎn)繞口.....)
var person = {
name : 'wang',
score:{
math:100,
English:100
}
}
在上面這個(gè)小栗子中,score屬性還是一個(gè)對(duì)象。
下面繼續(xù)來(lái)說(shuō)我們的拷貝,現(xiàn)在該深拷貝出場(chǎng)了......
深拷貝:對(duì)屬性中所有引用類型的值,遍歷到是基本類型的值為止,利用遞歸來(lái)實(shí)現(xiàn)深拷貝。
來(lái)看一個(gè)栗子
function cloneObject (obj) {
var newObj = {} //如果不是引用類型,直接返回
if (typeof (obj) !== 'object') {
return obj
}
//如果是引用類型,遍歷屬性
else{
for (var attr in obj) {
//如果某個(gè)屬性還是引用類型,遞歸調(diào)用
newObj[attr] = cloneObject(obj[attr])
}
}
return newObj
}
對(duì)于深拷貝,我們先判斷它是否為引用類型,如果不是,直接返回
如果是,循環(huán)遍歷該對(duì)象的屬性,如果某個(gè)屬性還是引用類型,則針對(duì)該屬性再次調(diào)用deepClone函數(shù)。