本次嘗試經過流程圖的形式並結合兩個例子來從新理解一下JavaScript中的參數傳遞。git
歡迎關注個人博客,不按期更新中——github
借用紅寶書的一句話:函數
ECMAScript中全部函數的參數都是按值傳遞的
這個值若是是簡單類型,那麼就是其自己。若是是引用類型也就是對象傳遞的就是指向這個對象的地址。故咱們能夠認爲參數傳遞所有都是值傳遞,那麼具體怎麼理解呢?看下例子:spa
var obj = { n: 1 }; function foo(data) { data = 2; console.log(data); //2 } foo(obj); console.log(obj.n) // 1
先不說爲何緣由,咱們就經過畫圖的方式來走一遍流程,我相信應該就能理解其中的參數傳遞了。切記傳遞引用類型傳遞的是指針!
首先執行var obj = {n: 1};
,能夠看做在棧的001地址中存入了一個指向{n:1}
的指針*p
3d
接下來爲聲明function foo
此時會建立函數執行上下文,產生一個變量對象,其中聲明瞭形參data,因爲函數沒有執行,當前值爲undefined。咱們記data地址爲022。關於更多變量對象的知識能夠參考冴羽老師的這篇JavaScript深刻之變量對象,本文不深刻研究關於AO相關,你只須要知道在聲明這個函數的時候裏面的形參已經被建立出來了。
執行foo(obj)
其中會進行參數傳遞,其中將obj中存儲的*p拷貝給處在022地址的data,那麼此時它們就指向了同一個對象,若是某一個變量更改了n的值,另外一個變量中n的值也會更改,由於其中保存的是指針。
指針
進入函數內部,順序執行data = 2;
此時002地址存儲了基本類型值,則直接存儲在棧中,從而與堆中的{n:1}失去了聯繫。從而打印console.log(data) // 2
,最後發現初始開闢的{n:1}對象沒有過更改,故而 console.log(obj.n) // 1
仍然打印1。code
var obj = {n:1}; (function(obj){ console.log(obj.n); //1 obj.n=3; var obj = {n:2}; console.log(obj.n) //2 })(obj); console.log(obj.n) //3
總體來看這個例子中出現了同名覆蓋的問題。不太瞭解代碼如何執行的流程,可能會由於同名的關係而有些混亂,不過不要緊。只要按照上一個例子的流程圖中的執行過程,必定能夠得出正確的結果。
對象
聲明變量obj,地址爲011其中存入指向{n:1}的指針*p
blog
聲明函數,雖然同爲obj變量名,可是形參obj爲AO中的屬性,不會與全局形成覆蓋,其擁有新的地址記做022,在未執行前其值爲undefined。
ip
函數當即執行,此時將全局obj賦值給形參obj,咱們忽略這個重複命名的問題,其實就是將011中的 指針*p拷貝了一份給了022。同時執行第一個console.log(obj.n)
結果即爲1。
執行obj.n=3
,此時爲函數的形參即022中的obj來改變了對象內n的值。
最關鍵的一步:var obj = {n:2};
因爲對象命名的關係可能不少童鞋就會有點懵,但依然按照一樣的方式來分析便可,因爲使用了var那麼就是新聲明一個對象,從而會在棧中壓入新的地址記做033,其中存入了新的指針指向了新的對象{n:2}。從而以後打印的console.log(obj.n)
結果則應是新開闢的對象中的n的值。
最後打印 console.log(obj.n) //3
很顯然,全局的對象有過一次更改其值爲3。
至此咱們走完了上述兩段代碼涉及變量的全部「心路歷程」,因爲做者不是科班出身,這個圖中對於堆棧以及變量重名的描述可能不是很是的準確,有差錯的地方還望不吝賜教~重點是能理解我但願表達的意思就好。總的來講關鍵點就在於傳參的過程當中存在一次值的拷貝,同時若是賦值對象是引用類型傳入的是指針,明白這兩點以後再加上以前流程圖的分析相信再遇到相似的問題均可以有較爲一致的思路了。
慣例po做者的博客,不定時更新中——有問題歡迎在issues下交流。