咱們知道js執行環境有全局環境(window)和局部環境(通常指函數環境)之分。前端
var num = 1; function calc(){ var num = 2; }
上述代碼,雖然有兩個num變量,可是他們所在的執行環境倒是不一樣的,第一個num執行環境是全局執行環境(window),第二個執行環境是函數執行環境(calc函數)。瀏覽器
某個執行環境中的全部代碼執行完畢後,該環境被銷燬,保存在其中的全部變量和函數定義也隨之銷燬(全局執行環境直到應用程序退 出——例如關閉網頁或瀏覽器——時纔會被銷燬)。函數
每一個函數都有本身的執行環境。當執行流進入一個函數時,函數的環境就會被推入一個環境棧中。 而在函數執行以後,棧將其環境彈出,把控制權返回給以前的執行環境。spa
1、環境棧3d
隨着程序按前後順序執行的時候,會生成各類環境(一個全局環境和n個函數環境),這些環境是由一個環境棧維護的,看代碼:code
1 var sum = 0; 2 function a(){ 3 var sum = 1; 4 function b(){ 5 var sum = 2; 6 function c(){ 7 var sum = 3; 8 console.log(sum); 9 } 10 c(); 11 } 12 b(); 13 } 14 a();
對應環境棧:對象
2、做用域鏈blog
當代碼在一個環境中執行時,會建立變量對象的一個做用域鏈(scope chain)。做用域鏈的用途,是保證對執行環境有權訪問的全部變量和函數的有序訪問。做用域鏈的前端,始終都是當前執行的代碼所在環境的變量對象。ip
好比說,在c函數環境裏有一句:內存
console.log(sum);
這時,會在c函數環境的做用域鏈中搜索這個標識符(sum),搜索過程始終從做用域鏈的前端開始, 而後逐級地向後回溯,直至找到標識符爲止(若是找不到標識符,一般會致使錯誤發生)。
很幸運,一次就找到了,c環境裏就有sum標識符,那麼就直接輸出他的值(3);
若是c環境找不到,會繼續依次往下找:
好比下面的代碼將會輸出 1 :
var sum = 0; function a(){ var sum = 1; function b(){ function c(){ console.log(sum); } c(); } b(); } a();
3、塊級做用域
在ES6以前,是沒有塊級做用域的,請看代碼
for(var i = 1;i<10;i++){ /* dosomething() */ } console.log(i);//10 { var name = 'Jack'; } console.log(name);//Jack if(true){ var age = 18; } console.log(age);//18
若是是在 C、C++或 Java 中:
for 語句初始化變量的表達式所定義的變量,只會存在於循環的環境之中;
由花括號封閉的代碼塊都有本身的做用域,變量會在 if 語句執行完畢後被銷燬;
可是,在JavaScript中:
由 for 語句建立的變量 i 即便在 for 循環執行結束後,也依舊會存在 於循環外部的執行環境中;
if 語句中的變量聲明會將變量添加到當前的執行環境。
在ES6以後,提供了塊級做用域申明關鍵字 let ,let申明的變量只能在 { } 所在的做用域內使用 :
for(let i = 1;i<10;i++){ /* dosomething() */ } console.log(i); { let name = 'Jack'; } console.log(name); if(true){ let age = 18; } console.log(age);
控制檯輸出:
4、垃圾收集機制
正如你們所熟知的,咱們寫的每個變量都是存在於內存中的(具體是棧內存),可是系統內存畢竟有限,不能一直寫一直往內存裏面放,這尼瑪分分鐘內存泄漏。
因此對於那些已經沒用的變量(垃圾)須要進行清除處理。
垃圾收集器必須跟蹤哪一個變量有用哪一個變量沒用,對於再也不有用的變量打上標記,以備未來收 回其佔用的內存。
用於標識無用變量的策略可能會因實現而異,但具體到瀏覽器中的實現,則一般有兩 個策略。
Ⅰ.標記清除
當變量進入環境(例如,在函 數中聲明一個變量)時,就將這個變量標記爲「進入環境」。
而當變量離開環境時,則將其 標記爲「離開環境」。
從邏輯上講,永遠不能釋放進入環境的變量所佔用的內存,由於只要執行流進入相應的環境,就可能會用到它們。