執行環境(也就是常說的上下文)和作用域是js中很基礎也很重要的概念, 但在很多時候,特別是看其他的文件的時候,卻容易混淆概念,這篇文章試著梳理下執行環境和作用域的概念。
1、執行環境
執行環境定義了變數或函式有權訪問的其他資料,決定了它們各自的行為。每個執行環境都有一個相關聯的變數物件,這個物件裡面儲存了環境中定義的所有變數和函式。這個變數物件在編寫程式碼是不能訪問的(除了最外層的window物件),只有解析器在後臺處理才能使用。
執行環境可以分成兩種:全域性執行環境和函式執行環境。在執行js程式碼之前,預設都會建立一個全域性的執行環境,與之關聯的是window物件,裡面儲存了所有全域性變數和函式,直到頁面關閉時才銷燬。而當執行某個函式時,會建立一個活動物件,並把這個物件作為與該函式的執行環境關聯的變數物件,從而建立出函式的執行環境。函式的執行環境在函式執行完之後,就會被銷燬。
另外,需要提一句的是:在活動物件剛被建立時,物件中只有arguments物件一個屬性。
2、作用域
瞭解執行環境,就可以來說作用域了。
在js中,執行環境是用環境棧來管理的。最底層的是全域性執行環境,當執行到一個函式, 函式的執行環境就會被推入到環境棧中。如果在函式中繼續執行函式,那麼內部函式的執行環境就繼續被推入環境棧。例如下面的程式碼:
var name = 'window';
outer();
function outer(){
var name = 'outer';
inner();
//函式內部的函式
function inner(){
var name = 'inner';
console.log(name);
}
}複製程式碼
對應的環境棧如下:
環境棧中的變數物件,從上到下就組成一條作用域鏈, 用來保證對執行環境有權訪問的所有變數和函式的有序訪問。解析識別符號時,就沿著作用域鏈一級一級地搜尋,也就在環境棧中從上向下一個個物件搜尋,直到找到識別符號,就返回,否則就報錯。例如,上面的程式碼,執行後會輸出‘inner’,當把inner函式中的定義變數語句註釋之後就輸出‘outer’。
2、延長作用域鏈
在兩種情況下,雖然不是在執行函式,但也會在作用域鏈的前端臨時增加一個變數物件:
- try-catch語句的catch塊
- with 語句
在執行with 語句時,會將指定的物件新增到作用域鏈中。例如:
function getHost() {
var res = '';
with(location){
res = host;
}
return res;
}複製程式碼
在上面的程式碼中,執行with語句時,作用域鏈的最頂端是臨時新增的location物件,因此可以直接訪問location物件的host屬性獲取值。
在執行catch語句時,會建立一個新的變數物件(該物件中包含被丟擲的錯誤物件),並新增到作用域鏈的頂端。正因為這個原因,在js的程式設計中,如果不是必要的,不建議在程式碼中使用try-catch語句塊。
備註:
在第一個例子中,我們把inner函式定義在outer函式的內部,如果是定義在outer函式外部呢?會不會有什麼不同?原因是什麼?
寫在最後:
如果覺得我寫的文章對你有幫助,歡迎掃碼關注我的公眾號:海痕筆記
微訊號:haihenbiji