1. 程式人生 > >JS 作用域和作用域鏈

JS 作用域和作用域鏈

1. 作用域

作用域就是程式碼的執行環境,全域性執行環境就是全域性作用域,函式的執行環境就是私有作用域,它們都是棧記憶體。

執行環境定義了變數或函式有權訪問的其他資料,決定了它們各自的行為。每個執行環境都有一個與之關聯的變數物件,環境中定義的所有變數和函式都儲存在這個物件中。雖然我們編寫的程式碼無法訪問這個物件,但解析器在處理資料時會在後臺使用它。

全域性執行環境是最外圍的一個執行環境。根據 ECMAScript 實現所在的宿主環境不同,表示的執行環境的物件也不一樣。

  • 在 Web 瀏覽器中,全域性執行環境被認為是 window 物件,因此所有全域性變數和函式都是作為 window 物件的屬性和方法建立的
  • 在 NODE 環境中,全域性執行環境是 global 物件。

某個執行環境中所有的程式碼執行完畢後,該環境被銷燬,儲存在其中的所有變數和函式定義也隨之銷燬(全域性執行環境直到應用程式退出時,如關閉瀏覽器或網頁,才會被銷燬)

每個函式都有自己的執行環境。當執行流進入一個函式時,函式的環境就會被推入一個環境棧中。而在函式執行之後,棧將被環境彈出,把控制權返回給之前的執行環境。ECMAScript 程式中的執行流正是由這個方便的機制控制著。

概括來說:
作用域就是程式碼執行開闢棧記憶體

  • 私有作用域 —-> 函式執行都會形成一個私有作用域
  • 全域性作用域 —-> 頁面一開啟就會形成一個全域性的作用域
  • 私有變數 —-> 在私有作用域裡邊形成的變數 (通過 var 宣告; 形參)
  • 全域性變數 —-> 在全域性作用域形成的變數(var a = 12 或者函式內沒有宣告,直接賦值的變數)

2. 作用域鏈

當代碼在一個環境中執行時,會建立變數物件的一個作用域鏈(作用域形成的鏈條)

  • 作用域鏈的前端,始終都是當前執行的程式碼所在環境的變數物件
  • 作用域鏈中的下一個物件來自於外部環境,而在下一個變數物件則來自下一個外部環境,一直到全域性執行環境
  • 全域性執行環境的變數物件始終都是作用域鏈上的最後一個物件

內部環境可以通過作用域鏈訪問所有外部環境,但外部環境不能訪問內部環境的任何變數和函式。

var n = 10;
function outer(){
  function inner(){
    function center(){
      console.log(n);
    }
    center();
  }
  inner();
  var n = 15;
}
outer(); //=> undefined

如函式的執行,形成一個私有作用域,形參和當前私有作用域中宣告的變數都是私有變數,儲存在內部的一個變數物件中,其下一個外部環境可能是函式,也就包含了函式的內部變數物件,直到全域性作用域。

當在內部函式中,需要訪問一個變數的時候,首先會訪問函式本身的變數物件,是否有這個變數,如果沒有,那麼會繼續沿作用域鏈往上查詢,直到全域性作用域。如果在某個變數物件中找到則使用該變數物件中的變數值。

由於變數的查詢是沿著作用域鏈來實現的,所以也稱作用域鏈為變數查詢的機制

這個機制也說明了訪問區域性變數要比訪問全域性變數更快,因為中間的查詢過程更短。但是 JavaScript 引擎在優化識別符號查詢方面做得很好,因此這個差別可以忽略不計。