記憶體的分配(示意)
啟動程式,就需要分配記憶體給執行的程式。啟動瀏覽器,就會分配一定記憶體供瀏覽器使用,瀏覽器在會分配相應的記憶體供諸如HTML+CSS
,JS
,'HTTP','其他外掛、定時器`等模組使用,如下圖。
會有一部分記憶體供JS
模組使用,JS
一般會將JS程式碼儲存在程式碼區
,資料區
內,通過某種聯絡將程式碼和資料對應在一起,如下圖。
記憶體圖
示意圖
記憶體圖就是簡化模擬示意JS
使用的記憶體中的資料區中的情況,簡單的分為棧記憶體Stack
,堆記憶體Heap
,如下圖。明顯,左邊是原始程式碼
,中間是棧記憶體
,右邊是堆記憶體
。
使用
左邊原始程式碼定義一個變數,在棧記憶體中就會用64位
儲存一個值。如果原始程式碼中變數是非物件
,棧記憶體
中這個值就是直接值,堆記憶體
中沒有資料。如果原始程式碼中變數是物件
,棧記憶體
中就會儲存堆記憶體的地址(隨機),堆記憶體
中會儲存這個物件的所有內容。
看到上個圖,原始程式碼區裡最後一行O2=O
,將一個物件賦值給另一個變數時,實際上是將堆記憶體的地址
賦值給另一個變數,轉換如下圖,O2
在棧記憶體中的內容就變成了和O
一樣的堆記憶體地址。
應用例項
第一個
- 原始程式碼中定義變數
a=1
,Stack中儲存1 - 原始程式碼中定義變數
b=a
,Stack中儲存b的值和a一樣,為1 - 原始程式碼中賦值
b=2
,都是非物件,Stack中直接將b的值改為2,不影響a - 全程非物件,所以沒有涉及堆記憶體Heap
第二個
- 原始程式碼中定義變數
a={name:'a'}
,是個物件,隨機分配Heap地址(比如:31)並在Heap中儲存這個物件,在Stack中儲存這個Heap地址,比如ADDR 31
- 原始程式碼中定義變數
b=a
,是個存在的物件,將a的Stack值(ADDR 31)(Heap地址)賦給b的Stack值 - 原始程式碼中賦值
b=null
,賦給了b一個非物件,將b的Stack值改為null,不影響a - 全程操作Stack值,物件的Stack值為Heap地址
第三個
- 原始程式碼定義變數
a={n:1}
,隨機分配Heap地址(比如:34)並在Heap中儲存這個物件,在Stack中儲存這個Heap地址,比如ADDR 34
- 原始程式碼中定義變數
b=a
,是個存在的物件,將a的Stack值(ADDR 34)(Heap地址)賦給b的Stack值 -
a.x=a={n:2}
,這句話閱讀順序從左往右
。首先,在Heap 34
中新增新屬性x:a,現在a的值是ADDR 34
,所以新屬性相當於x:ADDR 34。然後,把{n:2}
賦值給a,因為是個新物件,所以重新分配Heap地址(ADDR 54),並把a的Stack值變更為新的Heap地址(ADDR 51)。 -
alert(a.x)
,現在a的值實際是ADDR 54
,ADDR 54
裡面是沒有x這個屬性的,所以返回undefined -
alert(b.x)
,b一直是ADDR 34
,其中x屬性值為ADDR 54
,所以是個物件,返回[object Object] - 總之就是,原始程式碼裡對物件的操作都在堆記憶體Heap中實現,對變數的操作都在棧記憶體Stack中實現。非物件的Stack值就是直接值,物件的Stack值是堆記憶體Heap的地址
第四個
- 原始程式碼中定義變數
a={name:'a'}
,Heap中儲存物件,Stack中儲存Heap地址(ADDR 101) - 原始程式碼
b=a
,把a的Stack值(Heap地址)賦給b的Stack值 - 原始程式碼
b={'name':'b'}
,賦給b一個新物件,Heap中儲存新物件,b的Stack值變更為新的Heap地址(ADDR 301) - 所以,
a.name
沒有變,還是a
第五個
- 原始程式碼中定義變數
a={name:'a'}
,Heap中儲存物件,Stack中儲存Heap地址(ADDR 51) - 原始程式碼
b=a
,把a的Stack值(Heap地址)賦給b的Stack值 - 原始程式碼設定
b.name=b
,就是ADDR 51
中的name值變更為b - 所以,
a.name
也就是ADDR 51
中的name值,等於b - 這裡,a和b的Stack值都指向同一Heap地址,
ADDR 51
,所以無論對那個進行操作,都是對一個東西做操作,所以會互相影響