一、JS執行上下文
執行上下文就是當前 JavaScript 代碼被解析和執行時所在環境的抽象概念, JavaScript 中運行任何的代碼都是在執行上下文中運行。
執行上下文類型分爲:全局執行上下文和函數執行上下文。執行上下文創建過程中,需要做以下幾件事:
(1)創建變量對象:首先初始化函數的參數arguments,提升函數聲明和變量聲明。
(2)創建作用域鏈(Scope Chain):在執行期上下文的創建階段,作用域鏈是在變量對象之後創建的。
(3)確定this的值,即 ResolveThisBinding
二、作用域
作用域就是變量和函數的可訪問範圍,控制這個變量或者函數可訪問行和生命週期。
作用域有兩種工作模型:詞法作用域和動態作用域,JS採用的是詞法作用域工作模型,詞法作用域意味着作用域是由書寫代碼時變量和函數聲明的位置決定的。( with 和 eval 能夠修改詞法作用域,但是不推薦使用,對此不做特別說明)
在 js 中是詞法作用域,意思就是你的變量函數的作用域是由你的編碼中的位置決定的,當然可以通過 apply、call、 bind 等函數進行修改。
在 ES6 之前,js 中的作用域分爲兩種:函數作用域和全局作用域,現在作用域分爲:全局作用域、函數作用域、塊級作用域。
全局作用域顧名思義,瀏覽器下就是 window ,作用域鏈的頂級就是它,那麼只要不是被函數包裹的變量或者函數,它的作用域就是全局。
而函數作用域,就是在函數的體內聲明的變量、函數及函數的參數,它們的作用域都是在這個函數內部。
三、JS執行上下文棧(後面簡稱執行棧)
執行棧,也叫做調用棧,具有 LIFO (後進先出) 結構,用於存儲在代碼執行期間創建的所有執行上下文。
規則如下:
首次運行JavaScript代碼的時候,會創建一個全局執行的上下文並Push到當前的執行棧中,每當發生函數調用,引擎都會爲該函數創建一個新的函數執行上下文並Push當前執行棧的棧頂。
當棧頂的函數運行完成後,其對應的函數執行上下文將會從執行棧中Pop出,上下文的控制權將移動到當前執行棧的下一個執行上下文。
四、作用域鏈
我們知道函數在執行時是有個執行棧,在函數執行的時候會創建執行環境,也就是執行上下文,在上下文中有個大對象,保存執行環境定義的變量和函數,在使用變量的時候,就會訪問這個大對象,這個對象會隨着函數的調用而創建,函數執行結束出棧而銷燬,那麼這些大對象組成一個鏈,就是作用域鏈。那麼函數內部未定義的變量,就會順着作用域鏈向上查找,一直找到同名的屬性。