幾個例子理解不同資料型別的堆疊記憶體處理
如有錯誤煩請指正
js程式碼的執行環境
- 瀏覽器 核心(引擎)
- node
- webview(hybrid,嵌入到手機app裡面,在app裡面執行)
- ...
下面通過幾個例子理解不同資料型別的堆疊記憶體處理
js如何執行(示例1)
var a = 12;
var b = a;
b = 13;
console.log(a);
瀏覽器能夠執行js程式碼,是因為瀏覽器會在計算機記憶體中分配出一塊記憶體,用來供程式碼執行,這塊記憶體叫棧記憶體,也叫Stack,或者ECStack(Execution Context Stack)執行環境棧。
為了區分是哪個區域(全域性或者函式等)下的程式碼執行,會產生一個執行上下文
在全域性環境下會產生 EC(G) :Execution Context (golbal) 全域性執行上下文,其中VO(G)全域性變數物件(Varibale Object)儲存全域性執行上下文宣告的變數,然後進入棧記憶體執行。
宣告變數的步驟
接著開始執行,先宣告變數,宣告變數有三步
var [變數] = [值]
- 先建立值(執行等號右邊)
- 基本資料型別是直接儲存在棧記憶體當中
- 引用型別的值,都是開闢一個單獨的記憶體空間(堆記憶體Heap)儲存資訊
- 宣告變數 declare
- 存放到當前上下文的變數物件中(VO/AO)
- 定義(賦值)變數:讓變數和值關聯到一起,也就是所謂的賦值操作,也叫定義(defined)或指標指向
- 所以
var n; //預設值是undefined 未定義
- 所以
所以var a = 12
步驟是
- 在記憶體中開闢空間,儲存
12
值 - 宣告變數
a
- 將
12
賦值給a
而var b = a
處理是
因為右側的a不是值,所以不需要第一步,不需要在棧裡面開闢空間,直接進行第二步宣告,然後執行第三步,關聯到12
值(指標)
b=13
的處理步驟是
- 在記憶體中開闢空間,儲存
13
值 - 因為
b
已經在當前上下文的變數物件中,所以不需要第二步宣告 - 將
13
賦值給b
結果:
總體執行邏輯:
js如何執行(示例2)
var a = {n: 12};
var b = a;
b['n'] = 13;
console.log(a.n);
當第一步建立的值是一個引用型別的值時候,值就沒法直接存到棧裡(沒有這麼大的空間)。當建立引用型別值的時候,會進行以下處理
- 在計算機記憶體中分配一個單獨的記憶體出來(堆記憶體 Heap)
- 這塊堆記憶體有一個16進位制的地址用來尋找
- 把物件中的鍵值對分別儲存到堆記憶體當中
- 把堆記憶體地址放置到棧中,供變數呼叫
這就是第一步,建立值的過程
第二步宣告。第三部賦值,將16進位制的地址賦值給變數
var b = a;
時,因為a
為 變數,所以不需要建立值,接著宣告b
,最後賦值,將棧中a
指向的地址也同樣賦值給b
,讓b
也指向那個16進位制地址
b['n'] = 13
執行原理:
b['n'] = 13
屬於物件的成員訪問
b
首先基於地址0x000000
找到堆記憶體- 把堆記憶體中成員為
n
的值改為13
console.log(a.n)
也屬於成員訪問 所以輸出13
順序如下
總結:基本資料型別和引用資料型別的區別?
基本型別的值直接儲存在棧記憶體當中,直接按照值操作,引用資料型別值是開闢單獨的堆記憶體儲存資訊的,將堆記憶體的地址存在棧當中,操作的都是引用地址
js如何執行(示例3)
var a = {n: 12};
var b = a;
b = {n: 13};
console.log(a.n);
當到b = {n: 13};
時
新開闢一個堆記憶體{n: 13}
,將地址指向b
GO global object 全域性物件
不同於VO,VO(G)是全域性變數物件,儲存當前上下文宣告的變數的
GO global object 全域性物件,是載入頁面預設形成的。在瀏覽器中,載入頁面時,在全域性上下文中會預設定義一個叫window
的物件,其中有setTimeout
,setInterval
等供js呼叫的屬性和方法
注意區分VO和GO
JavaScript 中有一個特殊的物件,稱為全域性物件(Global Object),它及其所有屬性都可以在程式的任何地方訪問,即全域性變數
在瀏覽器 JavaScript 中,通常window
是全域性物件, 而 Node。js 中的全域性物件是global
,所有全域性變數(除了global
本身以外)都是global
物件的屬性。
在 Node。js 我們可以直接訪問到global
的屬性,而不需要在應用中包含它。
global
,process
,__filename
,__dirname
js如何執行(示例4)
var a = {n: 1};
var b = a;
a.x = a = {n: 2};
console.log(a.x);
console.log(b);
var a = {n: 1};
var b = a;
前兩行程式碼執行如下,不在闡述:
要想理解a.x = a = {n: 2};
簡單說一下運算子優先順序
var a=12,b=13;
相當於
var a=12
var b=13
var a=b=13
相當於
b=13
var a=b//或者var a=13
正常計算都是從右到左處理的(當然第一步還是建立值)
但是不管是a.x=b=13
還是b=a.x=13
都要先計算a.x
因為優先順序比較高(成員訪問的優先順序為19,僅次於()
運算,運算子優先順序 )
a.x = a = {n: 2};
運算步驟如下
- 開闢記憶體,假設地址為0x000001
- 將地址放入棧中
a.x = 地址
a = 地址
所以現在a
指向0x000001,b
指向0x000000,即
a
為{n:2}
,b
為{n:1,x:{n:2}}
結果:
一個變數只可以關聯一個棧中的值,但是一個棧中的值,可以被多個變數關聯