看到一個這樣的問題javascript
function setName(obj) { obj.name = "Tom"; obj = new Object(); obj.name = "Greg" ; } var person = new Object(); setName(person); alert(person.name); //"Tom"
按照通常的理解第二次重寫name屬性會覆蓋原先的name,輸出應該是"Greg"纔對。
這個問題其實就是高程書上的原題,高程書的一些地方看了不少遍,可是每次重看都仍是會有新的理解,這裏理解的關鍵在於JavaScript函數的參數是按值傳遞的。
以前只是粗泛的瞭解參數按值傳遞,可是卻並無搞清楚本質,結合高程書和知乎的這個回答連接纔對這個問題算是真正理解了。java
簡單類型的值,它們的值直接存儲在變量訪問的位置,這是由於這些簡單類型佔據的空間是固定的,因此可將他們存儲在較小的內存區域 – 棧中。這樣存儲便於迅速查尋變量的值。每次複製都是一個單獨的副本,之間相互獨立git
var num1 = 5; var num2 = num1;
圖示github
引用類型的值存儲在堆(heap)中的對象,也就是說,存儲在變量處的值是一個指針(point),指向存儲對象的內存地址。這是由於:引用值的大小會改變,因此不能把它放在棧中,不然會下降變量查尋的速度。相反,放在變量的棧空間中的值是該對象存儲在堆中的地址。地址的大小是固定的,因此把它存儲在棧中對變量性能無任何負面影響。函數
var obj1 = new Object(); var obj2 = obj1; obj1.name = "Nicholas"; alert(obj2.name); //"Nicholas"
圖示性能
其實ECMAScript函數參數其實是函數的局部變量,所以本題中將一個對象做爲參數傳入時,複製了一個指針指向對象在堆內存中的內存地址。按值複製傳遞上覆制了一個指針變量,這個變量是按值傳遞的。
在調用函數內部將一個新的Object賦給obj以前內存狀況是這樣的:
若是是按引用傳遞,就會直接把第一個(也就是變量自己)整個傳遞進去(就不會有第二格的存在了)
再將新的Object賦給obj以後:
能夠看到obj指向新的Object內存地址,而person引用的仍然是原來的Object,而且在函數內部新建的局部對象會在函數執行完畢後銷燬,所以打印的name是第一個。
原文地址spa
《JavaScript高級程序設計》設計