執行上下文和做用域的理解

本文參考引自:深刻理解javascript原型和閉包(完結)
不得不說,這個系列文章是真的給人恍然頓悟的感受,寫的很是好,強烈推薦。感謝大佬!javascript

執行上下文

函數每調用一次,都會產生一個新的執行上下文環境。由於不一樣的調用可能就有不一樣的參數。html

function fn(x) {
  console.log(arguments)
  console.log(x)
}
fn(20)
fn(10) // 不一樣的調用可能有不一樣的參數

執行上下文棧

執行全局代碼時,會產生一個執行上下文環境,每次調用函數都又會執行上下文環境。當函數調用完成時,這個上下文環境以及其中的數據都會被消除(固然了閉包並不會乖乖就範),處於活動狀態的執行上下文環境只有一個java

// 這是一個壓棧出棧的過程--執行上下文棧
1 let a = 10, fn, // 一、進入全局上下文環境
2    bar = function(x) {
3      let b = 5
4      fn(x + b) // 三、進入fn函數上下文環境
5    }
6 fn = function(y) {
7  let c = 5
8  console.log(y + c)
9 }
10
11 bar(10) // 二、進入bar函數上下文環境

(1)執行代碼以前,首先建立全局上下文環境。(活動狀態)閉包

// 全局上下文環境
a: undefined
fn: undefined
bar: undefined
this: window

而後執行代碼,代碼到10行以前,上下文環境中的變量都在執行過程當中被賦值。函數

// 全局上下文環境
a: 10
fn: function
bar: function
this: window

(2)而後執行到11行,調用bar函數。
跳轉到bar函數內部,執行函數體語句以前,會建立一個新的執行上下文環境。this

// bar執行上下文環境
b: undefined
x: 10
arguments: [10]
this: window

而後將bar執行上下文環境壓棧,設置爲活動狀態(當前惟一)spa

(3)而後執行到第4行,調用fn函數。
調到fn函數內部,執行函數體語句以前,會建立一個新的執行上下文環境code

// fn執行上下文環境
c: undefined
y: 15
arguments: [15]
this: window

而後將fn執行上下文環境壓棧,設置爲活動狀態(當前惟一)htm

(4)fn執行完畢後,調用fn函數生成的fn上下文環境出棧,被銷燬。
而後bar執行完畢後,調用bar函數生成的上下文環境出棧,被銷燬。而後剩下全局上下文環境,出棧銷燬。對象

執行上下文環境切換過程

做用域

JS沒有塊級做用域,除了全局做用域,函數會建立本身的做用域。做用域在函數定義時就已經肯定了,不是在函數調用肯定(區別於執行上下文環境,固然this也是上下文環境裏的成分)

// 全局做用域
let x = 100
    // fn做用域
    function fn(x) {
        // bar做用域
        function bar(x) {
          console.log(x)
        }
    }

let f1 = fn(5)
let f2 = fn(10)

f1() // 5
f2() // 10

做用域只是一個「地盤」,其中沒有變量。變量是經過做用域對應的執行上下文環境中的變量對象來實現的。因此做用域是靜態觀念的,而執行上下文環境是動態上的,二者並不同。有閉包存在時,一個做用域存在兩個上下文環境也是有的。

同一個做用域下,對同一個函數的不一樣的調用會產生不一樣的執行上下文環境,繼而產生不一樣的變量的值,因此,做用域中變量的值是在執行過程當中肯定的,而做用域是在函數建立時就肯定的。

若是要查找一個做用域下某個變量的值,就須要找到這個做用域對應的執行上下文環境,再在其中找到變量的值。

做用域鏈

函數在定義的時候(不是調用的時候)就已經肯定了函數體內部自由變量的做用域。

自由變量:好比a,是在fn做用域使用,可是並無在fn做用域定義,這就是自由變量。

let a = 100
function fn() {
  let b = 20
  function bar() {
    console.log(a + b) // a是自由變量
  }
  return bar
}

let x = fn(), b = 200
x()

那麼自由變量是如何獲得的呢?這就引出了做用域鏈。

bar要取得a的值,就要到建立bar這個函數的做用域中取值(這裏是fn做用域),fn做用域也沒有a,就到建立fn這個函數的做用域中取值(這裏是全局做用域),找到了就結束了。這就是做用域鏈。

相關文章
相關標籤/搜索