JavaScript中的執行上下文,既然碰見了這篇圖文並茂的文章,乾脆看完吧!(系列四)

JavaScript中的執行上下文

本文主要會講解咱們常常看到的上下文知識點,旨在幫助本身和你們加深對它理解。本篇文章能夠避開了變量提高相關知識,是但願篇幅能夠控制在必定範圍,方便你們瀏覽,劇透一下《變量對象》會在下一篇和你們見面~ 持續更新,您的三連就是我最大的動力,虛心接受大佬們的批評和指點,共勉!javascript

目錄

前言

又到了修煉JavaScript內功的時候了,繼上一篇《從做用域到做用域鏈》以後,咱們來談一談執行上下文,在寫這篇文章的時候總感受沒法完整的將知識點串聯起來,因此但願你們也能提些建議哦,讓這篇文章更值得收藏、點贊哦~html

1、怎麼描述執行上下文

1.1 本節知識導圖:前端

在這裏插入圖片描述

1.2 若是描述執行上下文java

  1. 當函數執行時,會建立一個稱爲執行上下文的內部對象。一個執行上下文定義了一個函數執行時的環境;
  2. 當一個函數被調用時,會建立一個活動記錄(有時候也稱爲執行上下文)。這個記錄會包含函數在哪裏被調用(調用棧)、函數的調用方式、傳入的參數等信息 ;
  3. 每一個函數在被定義時,就會有一個[[scope]]屬性,這個屬性裏保存着做用域鏈,而執行的前一刻都會建立一個OA對象,這個對象就是執行上下文,這個OA對象會被插入[[scope]]中做用域鏈的最頂端,這個對象裏保存着函數體聲明的全部變量、參數和方法。一個OA對象的有序列表。

上述三條描述都符合執行上下文的一些特色,但側重點都不同。git

1.3 執行上下文的類型github

  1. 全局執行上下文:只有一個,瀏覽器中的全局對象就是 window 對象,this 指向這個全局對象。web

  2. 函數執行上下文:存在無數個,只有在函數被調用的時候纔會被建立,每次調用函數都會建立一個新的執行上下文。面試

  3. Eval 函數執行上下文: 指的是運行在 eval 函數中的代碼,不多用並且不建議使用。數組

2、執行棧(Execution context stack)

你們都明白,函數的執行順序和它的定義順序不要緊,但如何解釋,就須要從執行棧提及了。瀏覽器

2.1 本節知識導圖

在這裏插入圖片描述

2.2 描述執行棧

執行棧,也叫調用棧,具備 LIFO(後進先出)結構,用於存儲在代碼執行期間建立的全部執行上下文。

首次運行JS代碼時,會建立一個全局執行上下文並Push到當前的執行上下文棧中。每當發生函數調用,引擎都會爲該函數建立一個新的函數執行上下文並push到當前執行棧的棧頂。

當棧頂函數運行完成後,其對應的函數執行上下文將會從執行棧中pop出,上下文控制權將移到當前執行棧的下一個執行上下文。

接下來問題來了,咱們寫的函數多了去了,如何管理建立的那麼多執行上下文呢?

3、形象化執行棧

咱們利用圖片+文字描述的方式來解釋這樣幾段代碼:

3.1 爲了模擬執行上下文棧的行爲,讓咱們定義執行上下文棧爲一個數組:

var ECStack = [];
複製代碼

試想當 JavaScript 開始要解釋執行代碼的時候,最早遇到的就是全局代碼,因此初始化的時候首先就會向執行上下文棧壓入一個全局執行上下文,咱們用 globalContext 表示它,而且只有當整個應用程序結束的時候,ECStack 纔會被清空,因此程序結束以前, ECStack 最底部永遠有個globalContext

ECStack.push('globalContext');
ECStack // ["globalContext"]
複製代碼

如今 JavaScript 遇到下面的這段代碼了:

function fun1() {
    fun2();
}
function fun2() {
    fun3();
}
function fun3() {
    console.log('最後打印3')
}
fun1(); // 最後打印3
複製代碼

當執行一個函數的時候,就會建立一個執行上下文,而且壓入(push)執行上下文棧,當函數執行完畢的時候,就會將函數的執行上下文從棧中彈出(pop)。知道了這樣的工做原理,讓咱們來看看如何處理上面這段代碼:

// 僞代碼

// fun1()
ECStack.push(<fun1> functionContext);

// fun1中居然調用了fun2,還要建立fun2的執行上下文
ECStack.push(<fun2> functionContext);

// 擦,fun2還調用了fun3!
ECStack.push(<fun3> functionContext);

// fun3執行完畢
ECStack.pop();

// fun2執行完畢
ECStack.pop();

// fun1執行完畢
ECStack.pop();

// javascript接着執行下面的代碼,可是ECStack底層永遠有個globalContext
複製代碼

在這裏插入圖片描述

再看以下代碼:

console.log(1);

function father() {
    console.log(2);
    (function child() {
        console.log(3);
    }());
    console.log(4);
}
father();

console.log(5);
//會依次輸出 1 2 3 4 5
複製代碼

分析它的執行棧經歷了什麼:

在這裏插入圖片描述
其實到這裏咱們已經大體瞭解了執行棧在函數執行前->執行後的流程了,但下一篇文章咱們會詳細瞭解釋一下,感興趣的小夥伴不妨點個關注,不跑丟哦~
在這裏插入圖片描述

4、思考題

如今咱們已經瞭解了執行上下文棧是如何處理執行上下文的,因此讓咱們看看上篇文章《從做用域到做用域鏈》最後的問題:

var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f();
}
checkscope();
複製代碼
var scope = "global scope";
function checkscope(){
    var scope = "local scope";
    function f(){
        return scope;
    }
    return f;
}
checkscope()();
複製代碼

兩段代碼執行的結果同樣,可是兩段代碼究竟有哪些不一樣呢?

答案就是執行上下文棧的變化不同。

讓咱們模擬第一段代碼:

ECStack.push(<checkscope> functionContext);
ECStack.push(<f> functionContext);
ECStack.pop();
ECStack.pop();
複製代碼

讓咱們模擬第二段代碼:

ECStack.push(<checkscope> functionContext);
ECStack.pop();
ECStack.push(<f> functionContext);
ECStack.pop();
複製代碼

參考

5、寫在最後

JavaScript內功基礎部分已經總結到第四篇了,本系列大約會有15篇文章,都是咱們在面試最高頻的,但工做中經常被忽略的知識點。

JavaScript內功系列:

完整版被我放在了CSDN,感興趣的能夠過去看看哦~ 傳送門

  1. this、call、apply詳解,系列(一)
  2. 從原型到原型鏈,系列(二)
  3. 從做用域到做用域鏈,系列(三)
  4. JavaScript中的執行上下文(本文)
  5. 下一篇預告:執行上下文的「伴生姐妹」——變量對象

關於我

  • 花名:餘光
  • WX:j565017805
  • 沉迷JS,水平有限,虛心學習中

其餘沉澱

若是您看到了最後,不妨收藏、點贊、評論一下吧!!! 持續更新,您的三連就是我最大的動力,虛心接受大佬們的批評和指點,共勉!

相關文章
相關標籤/搜索