作者:蘇墨橘
鏈接:https://www.zhihu.com/question/27114726/answer/35481766
來源:知乎
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處。
數(shù)據(jù)類型
在 javascript 中數(shù)據(jù)類型可以分為兩類:
原始數(shù)據(jù)類型值 primitive type,比如Undefined,Null,Boolean,Number,String。
引用類型值,也就是對象類型 Object type,比如Object,Array,Function,Date等。
聲明變量時不同的內(nèi)存分配
原始值:存儲在棧(stack)中的簡單數(shù)據(jù)段,也就是說,它們的值直接存儲在變量訪問的位置。這是因為這些原始類型占據(jù)的空間是固定的,所以可將他們存儲在較小的內(nèi)存區(qū)域 – 棧中。這樣存儲便于迅速查尋變量的值。
引用值:存儲在堆(heap)中的對象,也就是說,存儲在變量處的值是一個指針(point),指向存儲對象的內(nèi)存地址。這是因為:引用值的大小會改變,所以不能把它放在棧中,否則會降低變量查尋的速度。相反,放在變量的棧空間中的值是該對象存儲在堆中的地址。地址的大小是固定的,所以把它存儲在棧中對變量性能無任何負面影響。
不同的內(nèi)存分配機制也帶來了不同的訪問機制
在javascript中是不允許直接訪問保存在堆內(nèi)存中的對象的,所以在訪問一個對象時,首先得到的是這個對象在堆內(nèi)存中的地址,然后再按照這個地址去獲得這個對象中的值,這就是傳說中的按引用訪問。而原始類型的值則是可以直接訪問到的。
復(fù)制變量時的不同
原始值:在將一個保存著原始值的變量復(fù)制給另一個變量時,會將原始值的副本賦值給新變量,此后這兩個變量是完全獨立的,他們只是擁有相同的value而已。
引用值:在將一個保存著對象內(nèi)存地址的變量復(fù)制給另一個變量時,會把這個內(nèi)存地址賦值給新變量,也就是說這兩個變量都指向了堆內(nèi)存中的同一個對象,他們中任何一個作出的改變都會反映在另一個身上。(這里要理解的一點就是,復(fù)制對象時并不會在堆內(nèi)存中新生成一個一模一樣的對象,只是多了一個保存指向這個對象指針的變量罷了)
參數(shù)傳遞的不同
首先我們應(yīng)該明確一點:ECMAScript中所有函數(shù)的參數(shù)都是按值來傳遞的。但是為什么涉及到原始類型與引用類型的值時仍然有區(qū)別呢,還不就是因為內(nèi)存分配時的差別。 (我對比了一下,這里和復(fù)制變量時遵循的機制完全一樣的嘛,你可以簡單地理解為傳遞參數(shù)的時候,就是把實參復(fù)制給形參的過程)
原始值:只是把變量里的值傳遞給參數(shù),之后參數(shù)和這個變量互不影響。
引用值:對象變量它里面的值是這個對象在堆內(nèi)存中的內(nèi)存地址,這一點你要時刻銘記在心!因此它傳遞的值也就是這個內(nèi)存地址,這也就是為什么函數(shù)內(nèi)部對這個參數(shù)的修改會體現(xiàn)在外部的原因了,因為它們都指向同一個對象呀。或許我這么說了以后你對書上的例子還是有點不太理解,那么請看圖吧:
所以,如果是按引用傳遞的話,是把第二格中的內(nèi)容(也就是變量本身)整個傳遞進去(就不會有第四格的存在了)。但事實是變量把它里面的值傳遞(復(fù)制)給了參數(shù),讓這個參數(shù)也指向原對象。因此如果在函數(shù)內(nèi)部給這個參數(shù)賦值另一個對象時,這個參數(shù)就會更改它的值為新對象的內(nèi)存地址指向新的對象,但此時原來的變量仍然指向原來的對象,這時候他們是相互獨立的;但如果這個參數(shù)是改變對象內(nèi)部的屬性的話,這個改變會體現(xiàn)在外部,因為他們共同指向的這個對象被修改了呀!來看下面這個例子吧:(傳說中的call by sharing)
var obj1 = {
?value:'111'
};
var obj2 = {
?value:'222'
};
function changeStuff(obj){
?obj.value = '333';
?obj = obj2;
?return obj.value;
}
var foo = changeStuff(obj1);
console.log(foo);// '222' 參數(shù)obj指向了新的對象obj2
console.log(obj1.value);//'333'
code里的注釋太小看不清,我移到這里來:
/* obj1仍然指向原來的對象,之所以value改變了, *是因為changeStuff里的第一條語句,這個時候obj是指向obj1的 . *再啰嗦一句,如果是按引用傳遞的話,這個時候obj1.value應(yīng)該是等于'222'的 */
好了,以上就是關(guān)于這個問題的全部解釋了。
各位有興趣的話可以去了解一下call by value ,call by reference call by sharing 等函數(shù)傳遞的機制call by sharing
還有stackoverflow上對于函數(shù)傳遞的這個問題解釋得相當(dāng)精辟,值得一看。(下面有鏈接)
參考:
ECMAScript 原始值和引用值
Is JavaScript a pass-by-reference or pass-by-value language?
紅寶書《javascript高級程序設(shè)計》P69-P71
------------2017-4-11更新 ----------