《JavaScript高級程序設計》筆記——chapter4 變量

前言
本章內容是充分理解閉包的必要背景知識,但是書中講解不太清楚(特別是函數傳參這一部分)。從網上找了些講解如下,需要每個字都要反覆閱讀,直至完全理解透徹。

ECMAScript包含兩個不同類型的值:
  • 原始數據類型值 ——primitive type ,比如 UndifinedNullBooleanNumberString
  • 引用類型值—— 也就是對象類型 Object type,比如ObjectArrayFunctionDate等。
檢測類型:
要檢測一個變量屬於什麼類型,第三章3.4.1中介紹有 typeof 操作符
但是它對於檢測引用類型的值用處不大。我們通常並不是想知道某個值是對象,而是想弄清它是什麼類型的對象。這時候我們就用到了  instanceof 操作符。
語法如下:
result = variable instanceof constructor
具體用法看第六章原型鏈時的介紹,這裏不再多說。

聲明變量時不同的內存分配
  • 原始值:存儲在棧(stack)中的簡單數據段,也就是說,它們的值直接存儲在變量訪問的位置。這是因爲這些原始類型佔據的空間是固定的,所以可將他們存儲在較小的內存區域 – 棧中。這樣存儲便於迅速查尋變量的值。
  • 引用值:存儲在堆(heap)中的對象,也就是說,存儲在變量處的值是一個指針(point),指向存儲對象的內存地址。這是因爲:引用值的大小會改變,所以不能把它放在棧中,否則會降低變量查尋的速度。相反,放在變量的棧空間中的值是該對象存儲在堆中的地址。地址的大小是固定的,所以把它存儲在棧中對變量性能無任何負面影響。
不同的內存分配機制也帶來了
不同的訪問機制
在javascript中是不允許直接訪問保存在堆內存中的對象的,所以在訪問一個對象時,首先得到的是這個對象在堆內存中的地址(這個地址是存儲在棧內存中的),然後再按照這個地址去獲得這個對象中的值,這就是傳說中的按引用訪問。而原始類型的值則是可以直接訪問到的。

複製變量時的不同
  • 原始值:在將一個保存着原始值的變量複製給另一個變量時,會將原始值的副本賦值給新變量,此後這兩個變量是完全獨立的,他們只是擁有相同的value而已
  •  引用值:在將一個保存着對象內存地址的變量複製給另一個變量時,會把這個內存地址賦值給新變量,也就是說這兩個變量都指向了堆內存中的同一個對象,他們中任何一個作出的改變都會反映在另一個身上。(這裏要理解的一點就是,複製對象時並不會在堆內存中新生成一個一模一樣的對象,只是多了一個保存指向這個對象指針的變量罷了)
參數傳遞的不同:
首先我們應該明確一點:ECMAScript中所有函數的參數都是按值來傳遞的。但是爲什麼涉及到原始類型與引用類型的值時仍然有區別呢,還不就是因爲內存分配時的差別。 (對比了一下,這裏和複製變量時遵循的機制完全一樣的,可以簡單地理解爲傳遞參數的時候,就是把實參複製給形參的過程)
  • 原始值:只是把變量裏的值傳遞給參數,之後參數和這個變量互不影響。
  • 引用值:對象變量它裏面的值是這個對象在堆內存中的內存地址,這一點你要時刻銘記在心!因此它傳遞的值也就是這個內存地址,這也就是爲什麼函數內部對這個參數的修改會體現在外部的原因了,因爲它們都指向同一個對象呀。或許這麼說了以後,對書上的例子還是有點不太理解,那麼請看圖吧:
所以,如果是按引用傳遞的話,是把第二格中的內容(也就是變量本身)整個傳遞進去(就不會有第四格的存在了)。但事實是變量把它裏面的值傳遞(複製)給了參數,讓這個參數也指向原對象。因此如果在函數內部給這個參數賦值另一個對象時,這個參數就會更改它的值爲新對象的內存地址指向新的對象,但此時原來的變量仍然指向原來的對象,這時候他們是相互獨立的;但如果這個參數是改變對象內部的屬性的話,這個改變會體現在外部,因爲他們共同指向的這個對象被修改了呀!來看下面這個例子吧:(傳說中的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' 參數obj指向了新的對象obj2
console.log(obj1.value);//'333'

/* obj1仍然指向原來的對象,之所以value改變了,
*是因爲changeStuff裏的第一條語句,這個時候obj是指向obj1的 .
*再囉嗦一句,如果是按引用傳遞的話,這個時候obj1.value應該是等於'222'的
*/

 stackoverflow上對於函數傳遞的這個問題解釋得相當精闢,值得一看。如下:

function changeStuff(a, b, c){
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};}

var num = 10;var obj1 = {item: "unchanged"};var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);    //10
console.log(obj1.item);  //changed  
console.log(obj2.item);  //unchanged

If it was pure pass by value, then changing obj1.item would have no effect on the obj1 outside of the function. If it was pure pass by reference, then everything would have changed. num would be 100, and obj2.item would read "changed".

Instead, the situation is that the item passed in is passed by value. But the item that is passed by value is itself a reference. Technically, this is called call-by-sharing.

In practical terms, this means that if you change the parameter itself (as with num and obj2), that won't affect the item that was fed into the parameter. But if you change the INTERNALS of the parameter, that will propagate back up (as with obj1).

 可以去了解一下call by value ,call by reference   call-by-sharing 等函數傳遞的機制 

參考:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章