Hello,上一篇(http://www.cnblogs.com/souvenir/p/4969399.html)我們簡單的分享了JS中的變量存儲原理,但是並未結束,我們漏掉了什么。
對了,就是關於對象的存儲。
大家都知道,JavaScript中的變量類型分為兩種,一種是基本數據類型,包括:undefined,null,Number,String,Boolean,另外一種就是對象。
兩種數據類型的存儲方式在JS中也有所不同。
另外,內存分為棧區(stack)和堆區(heap),然后在JS中開發人員並不能直接操作堆區,堆區數據由JS引擎操作完成,那這二者在存儲數據上到底有什么區別呢?
我們簡單的通過下面這張圖來分析:
JS中變量的定義在內存中包括三個部分:
-
- 變量標示 (比如上圖中的Str,變量標示存儲在內存的棧區)
- 變量值 (比如上面中的Str的值souvenir或者是obj1對象的指向堆區地址,這個值也是存儲在棧區)
- 對象 (比如上圖中的對象1或者對象2,對象存儲在堆區)
也就是說,對於基本數據類型來說,只使用了內存的棧區。
對於上一篇中提到的問題來說,
1 var a = 100; 2 3 func(); 4 5 function func(){ 6 console.log(a); 7 var a=200; 8 console.log(a); 9 }
在JS預加載階段,JS引擎只是在內存的棧區為每個變量分配了內存,指定了標示符,並未為其指定值。
等到JS執行期才會為其賦值。
現在我們再來看對象變量的問題就比較清楚了,例如下面的:
1 var a=100; 2 var obj1={ 3 attr:'hello' 4 }; 5 6 func(a,obj1); 7 8 function func(num,obj){ 9 var a2=num; 10 a2=200; 11 12 var obj2=obj; 13 obj2.attr='hello'; 14 } 15 16 console.log(a); 17 console.log(obj1.attr);
我們分別定義了一個基本類型和對象類型的變量,然后在函數中對其分別執行復制操作,然后修改新變量的值。
最后的執行結果為:
對於基本數據類型,在執行第9行時,JS是把num在棧區的值,也就是100復制給了a2這個局部變量,然后在第10行又修改了a2的值,
這個操作過程並未影響到全局變量a的值。
小結:
對於對象來說,當JS執行12行的時候,實際上是把obj在棧區的值,也就是obj對象在堆區的引用地址,復制給了新的局部變量obj2,
這時候,obj2與obj實際上已經指向了同一個堆區的對象,然后obj2修改了這個對象的某個屬性值。然后函數執行完畢,obj2這個局部變量沒有引用將會被GC回收。
再次訪問obj1這個全局變量時,其所指向的對象其實已經被修改過了。