Js系列二:記憶體空間
因為js有垃圾自動回收機制,所以記憶體空間不是一個被經常提及的概念,也很容易被忽略,特別是非工科內入行的前端通常對記憶體機制比較模糊甚至一無所知。然而記憶體空間確是真正的基礎,是我們理解其他概念的基石,如閉包等。所以我們需要花點時間來理解一下它。
一,基礎資料型別與變數物件
最新的ECMAScript標準定義了7種資料型別,其中6種基礎資料型別和一種引用資料型別(Object):如下表所示。
型別 | 值 |
Boolean | 只有兩個值:true和false |
Null | 只有一個值:null |
Undefined | 只有一個值:undefined |
Number | 所有的數字 |
String | 所有的字串 |
Symbol | 符號型別var sym=Symbol('testsymbol') |
由於Symbol還存在一定的相容性問題。即使經過Babel編譯之後程式碼量過大。因此不建議在實踐中使用。
下面我們來看一個簡單的列子
function fn(){
var a1=10;
var a2 = 'hello';
var a3 = 'null';
}
現在我們思考當函式執行的時候,a1,a2,a3都儲存在什麼地方?
函式執行的時候回建立一個執行環境,這個執行環境被稱為執行上下文。在執行上下文中,會建立一個變數物件(VO)的特殊物件。基礎資料都儲存到變數物件VO中(執行上下文,變數物件VO我們會在接下來的系列中講解),如圖:
變數名 | 具體值 |
a1 | 10 |
a2 | 'hello' |
a3 | 'null' |
變數物件存在於堆記憶體當中,因為變數物件尤其特殊的職能,因此在理解時建議仍然將其與堆記憶體空間區分開來。
二,引用資料型別與堆記憶體空間
引用資料型別(Object)是儲存在堆記憶體空間中的物件,在Js中不允許直接訪問對記憶體空間中的資料,因此不能直接操作物件的記憶體空間。在操作操作物件時,實際上是在操作物件的引用,因此引用資料型別都是按引用訪問的,這裡的引用我們可以理解為儲存物件的一個地址,改地址與堆記憶體空間中的物件相關聯。
為了更好的理解對記憶體空間,下面我們用一個列子來配合講解。
function foo(){
var num=10;
var bol = true;
var str = abc;
var person={
name:'jxl',
age:20,
id:100,
};
var arr=[a,b,c]
}
當我們要訪問堆記憶體空間中的資料時實際上是通過一個引用(地址指標)來訪問的
我們面試的時候經常會遇到這樣的題目
//demo1
var a=20;
var b=a;
b=30;
//這時a的值是多少
//demo2
var m={a:10,b:20};
var n=m;
n.a=15;
//這時的m.a是多少?
在demo1中資料型別發生了一次複製行為,在demo2中資料型別發生了一次複製行為
當變數物件的資料發生複製行為時,新的變數會被分配到一個新的值,在demo1中,通過var b =a發生複製之後,雖然a與b的值都等於20,但是其實是相互獨立相互不影響的值了,因此當我們修改了b的值後a的值不會發生變化。具體如圖
a | 20 |
a | 20 |
b | 20 |
a | 20 |
b | 30 |
的demo中通過var n=m發生了一次複製行為。引用型別的複製同樣會為新的變數自動分配一個新的值並儲存在變數物件中,但不同的是,這新值,僅僅只是引用型別的一個地址指標,當地址指標相同時儘管他們相互獨立,但是他們指向的物件實際上是同一個因此當n變化時,m的值也會發生變化這就是引用型別的特性。
三,記憶體空間的管理
因為自動垃圾回收機制的存在,使我們在開發時不用關心記憶體機制的使用問題,記憶體的分配回收完全實現了自動化管理。但是認識了記憶體機制之後能使我們在寫程式碼的過程當中清楚的知道自己寫的程式碼在執行的過程當中都發生了什麼,從而寫出更加優秀的程式碼。在成為優秀前端工程師的路上,關心記憶體管理是一件非常有必要的事情。
下面我們通過一個簡單的列子來了解記憶體空間的使用過程。
var a=20;
alert(a+100);
a = null;
上面三天語句分別對應如下的三個過程
(1)分配記憶體
(2)使用分配到的記憶體
(3)不需要時釋放記憶體
其實前面兩條都比較好理解,我們需要重點關注的是第三個過程,這裡涉及Js垃圾回收機制的實現原理。
JS垃圾回收機制需要用的"引用"的概念。當一個記憶體空間重間中的地址能夠被訪問時,垃圾回收器就會認為"該資料能夠被獲得"。不能被獲得的資料就會被打上標記,並回收記憶體空間。這種標記方式叫作:標記--清除演算法。
這個演算法會設定一個全域性物件,並定期從全域性物件開始查詢。垃圾回收器會找到所有可以被獲得與不能被獲得的資料。因此在上面的列子當中,當a被設定為null時,那麼剛開始分配的20變成不可訪問,而很快被自動回收
注意:在區域性作用域中,當函式執行完畢之後,區域性作用域就沒有存在的必要了,因此垃圾回收機制很容易的做出判斷並回收。但在全域性中,變數什麼時候需要自動釋放空間則很難判斷,因此在開發時,應儘量使用區域性變數避免使用全域性變數,如果使用了全域性變數則建議在不使用它時,通過a=null這樣的方式手動釋放引用,以確保能夠及時回收記憶體空間。