js中參數傳遞爲何一定是按值傳遞,有些疑惑,如下是一些理解。函數
js高級程序設計(第三版)的第四章,代表了基本數據類型(Undefined、Null、Boolean、Number、String)是按值訪問的,對象是按引用訪問的。spa
首先,看下引用類型變量是怎樣的(obj一、obj2都是對象類型):設計
var obj1 = new Object(); var obj2 = obj1;
簡單來講,obj一、obj2都是變量,他們分別有一個指針,各自指向各自的棧內存,而後棧內存中存放有堆內存地址和一些別的數據,那麼就又有第二個指針根據棧內存中存放的地址指向堆內存,堆內存中存放的是同一個的對象(由於obj1和obj2棧內存中的地址是相同的)。指針
注意到書中原話:code
當從一個變量向另外一個變量複製引用類型的值時,一樣也會將存儲在變量對象中的值複製一份放到爲新變量分配的空間中。不一樣的是,這個值的副本其實是一個指針,而這個指針指向存儲在堆中的一個對象。複製操做結束後,兩個變量實際上將引用同一個對象。所以,改變其中一個變量,就會影響另外一個變量。
而後,實驗代碼以下:對象
var t1 = new Object(); var t2 = t1; t2.name ="t1"; t2 = new Object(); //本質爲t2的指針指向了一個新空間 console.log(t1.name); //輸出t1 console.log(t2.name); //輸出undefined
分析:ip
var t2 = t1;
這行代碼實質是把t1棧內存中的數據複製在t2的棧內存中,棧內存中存放的堆內存地址就是同樣的,於是指向了同一個堆內存地址,即指向同一個對象。內存
t2 = new Object();
這行代碼就是把t2在棧內存中存放的地址改變了,變成了一個此刻不在使用的地址,而後t2的指針指向了這個新地址,因此以後t1和t2無關了。it
能夠看出,書中寫的 複製操做結束後,兩個變量實際上將引用同一個對象。 指兩個變量是兩個獨立的變量,惟獨在於他們各自的對象都是在同一個堆內存空間的。我通俗理解爲:t1和t2兩個變量自己是按值訪問的,t1和t2指向的對象是按引用訪問的,但實際上定義變量,天然是爲了操做變量指向的對象。io
下面是對於函數參數傳遞中的一些理解:
先看一下,讓人誤覺得參數是引用傳遞的代碼:
var t1 = new Object(); function change(obj){ obj.name = "lalala"; } change(t1); console.log(t1.name); //輸出了lalala
結果是外部的t1多了一個屬性name,緣由就在於確實是參數傳遞,把t1按值傳遞給了obj,操做他們各自屬性的時候,屬性仍然是按引用傳遞的,咱們傳的參數又不是t1的屬性。
強調一下,我上面說t1的屬性,是不正確的說法,嚴格來講,不是t1多了一個屬性,也不是t1的屬性,而是t1指向的對象多了一個屬性,或者說那是t1指向的對象的屬性,t1只是一個變量罷了。
再看下面代碼:
var t1 = new Object(); function change(obj){ obj.name = "t1"; obj = new Object(); obj.name = "new t1"; } change(t1); console.log(t1.name); //輸出t1
輸出結果,t1.name仍然是‘‘t1’’,再進行obj = new Object()時候,由於棧內存中數據的改變,這裏的obj最終指向的對象不是t1指向的對象了,就函數內部再也操做不到t1一開始指向的對象了,相似於下面代碼中t2的從新聲明。
var t1 = new Object(); var t2 = t1; t2.name ="t1"; t2 = new Object(); console.log(t1.name); //輸出t1 console.log(t2.name); //輸出undefined
結論:函數的參數傳遞是按值傳遞的,但對於參數是引用類型變量,該變量指向的對象的屬性(就是t1.name之類的)是按引用訪問的,所以改變對象的屬性會反映在外部對象上;但變量自己按值傳遞了,所以改變內部變量(就只改變t一、t2),外部變量不會有改變。就是實際上沒有把堆內存中的對象做爲函數參數。