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這個全域性變數時,其所指向的物件其實已經被修改過了。