本文參考引自:深刻理解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這個函數的做用域中取值(這裏是全局做用域),找到了就結束了。這就是做用域鏈。