JavaScript 執行上下文和執行棧

執行上下文是什麼和爲何要了解

execution context,是 Javascript 代碼被解析和執行所在環境的抽像概念。做爲一個前端工程師,除了碼代碼,還得要知道代碼是如何運行的,好比 Javascript 引擎和執行上下文以及調出堆棧之間是怎麼工做的;javascript

類型分類

全局執行上下文

能夠這麼理解,不在函數中的代碼都在全局執行上下文中。在此期間,共發生兩件事:前端

  • 建立一個全局對象,這個全局對象就是瀏覽器中 window;
  • this 指針指向這個對象;

注:一個程序中只有一個全局執行上下文對象,否則就亂套了。java

函數執行上下文

在函數調用時建立。想一想一個程序能夠有多少個函數,沒人定義,因此函數上下文是沒個數限制的。node

Eval 函數執行上下文

運行 Eval 函數中的代碼,因爲此函數不被 Javascript 開發人員待見,因此接下來的內容就不帶它玩了。數組

總結下,不在函數裏的就是全局上下文,其餘都是函數執行上下文(由於咱們不帶 Eval 函數執行上下文)。瀏覽器

執行上下文棧(Execution Context Stack)

棧,具備後進先出結構(LIFO, Last In First Out),具備 push 和 pop 功能。代碼執行期間所建立的執行上下文就是它存儲的。這麼一說,它的做用就提現出來了。那存儲執行上下文就是執行上下文棧咯。前端工程師

問題來了,全局執行上下文和函數執行上下文是何時進入執行棧中的呢?app

  • 全局執行上下文:當 Javascript 引擎首次讀取腳本時建立並推入當前棧;
  • 函數執行上下文:當函數被調用時,Javascript 引擎會建立一個新的執行上下文(由於會有無數個)並推入當前棧;

接下來,Javascript 引擎會執行棧頂端的函數,函數執行完成後就會從棧中彈出,而後執行下一個。函數

// context-stack-01.js
let name = 'pr';

const firstFunction = () => {
    console.log('first function');
    secondFunction();
    console.log('first function again');
}

const secondFunction = () => {
    console.log('second function');
}

firstFunction();

console.log(name);

// first function
// second function
// first function again
// pr
複製代碼

從代碼和流程圖可見post

  • 代碼在瀏覽器加載時,引擎建立一個全局執行上下文,入棧;
  • firstFunction 函數調用時(此時函數內部代碼還未執行),引擎建立一個新的函數執行上下文,而後入棧;
  • 接着 secondFunction 函數調用時(此時函數內部代碼還未執行),引擎建立一個新的函數執行上下文,而後入棧;
  • secondFunction 函數執行完後,函數執行上下文出棧(銷燬);
  • 接着 firstFunction 函數執行完後,函數執行上下文出棧(銷燬);
  • 最後所有代碼執行完後,全局執行上下文出棧(瀏覽器關閉時);

爲了便於理解,用數組模擬下執行上下文棧的行爲

// ESC = execution context stack
ESC = [ globalContext ];
ESC.push(functionContext<firstFunction>);
ESC.push(functionContext<secondFunction>);
ESC.pop();  // secondFunction();
ESC.pop();  // firstFunction();
// globalContext, 直到程序結束才被移除
複製代碼

總結

  • 執行棧就這樣在擁有和失去中度過(感受好可憐);
  • 執行棧和 Javascript 引擎是好兄弟(姐妹);

上下文的屬性

變量對象

簡稱 VO(Variable object),是與執行上下文相關的數據域,存儲了在上下文中定義的變量和函數聲明。

咱們已經知道執行上下文分全局執行上下文和函數執行上下文,既然執行上下文都有變量對象,那對應就有全局執行上下文變量對象和函數執行上下文變量對象

全局執行上下文變量對象

這個名字有點長,可是若是叫全局對象,你確定就以爲熟悉,其實二者是一回事,可使用 this 來訪問(瀏覽器環境)。

瀏覽器環境 this 指向 window,node 環境 this 指向 global。

函數執行上下文變量對象

變量對象咱們知道是 VO,函數執行上下文變量對象能夠叫活動對象(activation object,這裏簡稱 AO),這麼稱呼的緣由是當進入函數執行上下文中,這個執行上下文才被激活,此刻屬性才能被訪問。

做用域鏈

咱們知道代碼是一段一段執行的,這裏一段代碼指的是執行上下文,而一個程序中有一個全局執行上下文和無數個函數執行上下文(Eval 函數執行上下文咱們不帶它玩)。因此,一般在函數中,查找變量會先從當前執行上下文的 VO 中查找,若是沒有就到其父級執行上下文的 VO 查找,直至全局執行上下文的 VO。這流程所造成的鏈表就是做用域鏈(Scope Chain)(是否是有點像原型鏈,畢竟都是鏈嘛)。

this 指向

Javascript 特有的一套玩法,不一樣場景下,指向的對象就不一樣。

  • 全局範圍 this - 全局對象;
  • 函數調用 this - 全局對象;
  • 對象中的方法調用 this - 該方法所在的對象(函數別名除外);
  • 構造函數調用 this - 新建立的對象;
  • 顯式設置 apply/call/bind this - 函數第一個參數(即所綁定對象);

第3點中有個函數別名除外,這裏解釋下

var name = 'I am window';
let obj = {
    name: 'I am obj',
    fn: function(str) {
        console.log(str, this.name);
    }
}
const fn = obj.fn;
obj.fn('obj.fn =>'); // obj.fn => I am obj
fn('fn =>');  // fn => I am window
複製代碼

一個字,誰調用了,this 就指向誰(誰動我打誰,哈哈)。

你能夠

上一篇:Javascript 堆、棧和隊列

下一篇:Javascript 函數節流

相關文章
相關標籤/搜索