var a;
function
b(){}
那麼它們具體儲存在**,又是怎麼執行的呢?
這次,就聊一聊 js 的堆疊記憶體和變數的建立機制。(這裡只介紹 es5 的機制)
在了解變數建立機制之前,先來了解一下變數的儲存空間。
儲存空間分為棧記憶體和堆記憶體。
棧記憶體:作用域
- 提供乙個供 js **自上而下執行的環境(**都是在棧記憶體中執行的)
- 儲存基本型別值。由於基本型別比較簡單,它們都是直接在棧記憶體中開闢乙個位置,直接把值儲存進去的
堆記憶體:引用值對應的空間
- 物件:鍵值對
- 函式:**字串
由於引用型別的值可能過於複雜,所以需要另外開闢空間來儲存,而變數中儲存的只是指向這個空間的位址。
(1)建立作用域
當瀏覽器(核心/引擎)渲染和解析 js **的時候,會提供乙個供 js **執行的環境,這個環境稱為「全域性作用域」(global / window scope),是乙個棧記憶體
(2)進行變數提公升
將在作用域中使用 var / function 宣告的變數進行提公升。其中
- var 宣告的變數只提公升宣告,不定義
- function 宣告的變數,既提公升宣告,也提公升定義
所以,函式在當前作用域的任何地方都可以使用。
(2)**自上而下執行
基本資料型別的值會儲存在當前作用域下,以var a = 12
為例
- 首先在當前作用域中宣告乙個變數 a(這一步會在變數提公升階段完成,執行時會忽略這個宣告)
- 然後開闢乙個空間儲存值 12
- 最後讓宣告的變數與儲存的值進行關聯(就是賦值操作,也叫做定義)
基本資料型別(也叫做值型別),是按照值來操作的:把原有的值賦值乙份放到新的空間或者位置上,和原來的值沒有關係
引用資料型別的值,我們需要開闢乙個新的空間(理解為倉庫),把內容儲存到這個空間中
- 首先宣告乙個變數(同樣,這一步是在變數提公升階段完成,執行時會忽略這個宣告)
- 然後開闢乙個新的記憶體空間,把物件中的鍵值對依次儲存起來(此空間有個 16 進製的位址)
- 讓變數與空間位址關聯起來(把空間位址賦值給變數)
var obj = ;
console.log(obj.m);
原因分析
1. 形成乙個全域性作用域(棧記憶體)
2. **自上而下執行
- 首先開闢乙個新的堆記憶體,把鍵值對儲存到對記憶體中n: 10
,m: obj.n * 10
- 此時堆記憶體資訊還沒有儲存完成,空間位址還沒有與變數obj
關聯,此時的obj
是undefined
,obj.n <=> undefined.n
,所以報錯
var obj = ;
obj.m = obj.n * 10; // 此時的 obj 已經有值了
console.log(obj.m); //=> 100
var arr1 = [3, 4];
var arr2 = arr1;
arr2[0] = 1;
arr2 = [4, 5];
arr2[1] = 2;
arr1[1] = 0;
console.log(arr1, arr2); //=> 1, 0, 4, 2
原因分析:
1. 陣列也是物件,屬於引用型別,會開闢乙個新的堆記憶體儲存[3,4]
2. 往下,arr2 = arr1
,此時兩個變數同時儲存乙個堆記憶體的位址
3. 改變 arr2 會反映到 arr1,此時 arr1:[1,4]
arr2:[1,4]
4. 新開闢乙個堆記憶體,儲存[4,5]
,然後再把位址賦值給 arr2
5. 此時兩個變數關聯的記憶體不再一樣,對其的操作不再相互影響,此時 arr1:[1,4]
arr2:[4,5]
6. 再次賦值後,arr1:[1,0]
arr2:[4,2]
js變數提公升機制
宣告 declare var a function sum 預設值undefined 定義 defined a 12 定義其實就是賦值操作 變數提公升階段 帶 var 的只宣告未定義 帶 function 的宣告 和賦值都完成了。變數提公升只發生在當前作用域 例如 開始載入頁面的時候,只對全域性作用...
js 動態建立變數
通過eval 實現 宣告乙個函式 explain 傳參個數不確定 function test alert param1 呼叫test marydon 動態建立變數演示 和平常意義上的變數宣告和賦值一樣,eval 函式裡面直接放字串就行了,需要變動的是將其全部當成字串來處理,涉及到變數的地方,需要使用...
js 動態建立變數
通過eval 實現 宣告乙個函式 explain 傳參個數不確定 function test alert param1 呼叫test marydon 動態建立變數演示 和平常意義上的變數宣告和賦值一樣,eval 函式裡面直接放字串就行了,需要變動的是將其全部當成字串來處理,涉及到變數的地方,需要使用...