JS 調用棧


JS 是一門單線程語言

javascript事件循環

1.既然js是單線程,那就像只有一個窗口的銀行,客戶需要排隊一個一個辦理業務,同理js任務也要一個一個順序執行。如果一個任務耗時過長,那么后一個任務也必須等着。那么問題來了,假如我們想瀏覽新聞,但是新聞包含的超清圖片加載很慢,難道我們的網頁要一直卡着直到圖片完全顯示出來?因此聰明的程序員將任務分為兩類:同步任務和異步任務

執行上下文

1.執行上下文是評估和執行 JavaScript 代碼的環境的抽象概念。每當 Javascript 代碼在運行的時候,它都是在執行上下文中運行。
2.類型
    2.1 全局執行上下文 — 這是默認或者說基礎的上下文,任何不在函數內部的代碼都在全局上下文中。它會執行兩件事:創建一個全局的 window 對象(瀏覽器的情況下),並且設置 this 的值等於這個全局對象。一個程序中只會有一個全局執行上下文。
    2.2 函數執行上下文 — 每當一個函數被調用時, 都會為該函數創建一個新的上下文。每個函數都有它自己的執行上下文,不過是在函數被調用時創建的。函數上下文可以有任意多個。每當一個新的執行上下文被創建,它會按定義的順序(將在后文討論)執行一系列步驟。
    2.3 Eval 函數執行上下文 — 執行在 eval 函數內部的代碼也會有它屬於自己的執行上下文,但由於 JavaScript 開發者並不經常使用 eval,所以在這里我不會討論它。
3.執行棧(調用棧):
    3.1 當 JavaScript 引擎第一次遇到你的腳本時,它會創建一個全局的執行上下文並且壓入當前執行棧。每當引擎遇到一個函數調用,它會為該函數創建一個新的執行上下文並壓入棧的頂部。
    3.2 引擎會執行那些執行上下文位於棧頂的函數。當該函數執行結束時,執行上下文從棧中彈出,控制流程到達當前棧中的下一個上下文。
4.怎么創建執行上下文
    4.1 創建執行上下文有兩個階段:1) 創建階段 和 2) 執行階段。
    4.2 創建階段會發生三件事:
        4.2.1 this 值的決定,即我們所熟知的 This 綁定。
            4.2.1.1 在全局執行上下文中,this 的值指向全局對象。(在瀏覽器中,this引用 Window 對象)。
            4.2.1.2 在函數執行上下文中,this 的值取決於該函數是如何被調用的。如果它被一個引用對象調用,那么 this 會被設置成那個對象,否則 this 的值被設置為全局對象或者 undefined(在嚴格模式下)
               function A(){
                    b:function(){
                        console.log(this);
                    }
                }
                A.b();//{b:f},this引用的是A,因為b在A中被調用
                var b=A.b;
                b();//windowd對象,this指向的是全局對象window,因為沒有指向引用對象

 

        4.2.2 創建詞法環境組件。
            4.2.2.1 官方的 ES6 文檔把詞法環境定義為:詞法環境是一種規范類型,基於 ECMAScript 代碼的詞法嵌套結構來定義標識符和具體變量和函數的關聯。一個詞法環境由環境記錄器和一個可能的引用外部詞法環境的空值組成。簡單來說詞法環境是一種持有標識符—變量映射的結構。(這里的標識符指的是變量/函數的名字,而變量是對實際對象[包含函數類型對象]或原始數據的引用)。
            4.2.2.2 在詞法環境的內部有兩個組件:(1) 環境記錄器和 (2) 一個外部環境的引用。
                a 環境記錄器:存儲變量和函數聲明的實際位置。
                    類型 1.聲明式環境記錄器存儲變量、函數和參數。
                         2.對象環境記錄器用來定義出現在全局上下文中的變量和函數的關系。
                b 外部環境的引用意味着它可以訪問其父級詞法環境(作用域)。
            4.2.2.3 類型:
                a 全局環境(在全局執行上下文中)是沒有外部環境引用的詞法環境。全局環境的外部環境引用是 null。它擁有內建的 Object/Array/等、在環境記錄器內的原型函數(關聯全局對象,比如 window 對象)還有任何用戶定義的全局變量,並且 this的值指向全局對象。
                b 在函數環境中,函數內部用戶定義的變量存儲在環境記錄器中。並且引用的外部環境可能是全局環境,或者任何包含此內部函數的外部函數。
                c 在全局環境中,環境記錄器是對象環境記錄器;在函數環境中,環境記錄器是聲明式環境記錄器。對於函數環境,聲明式環境記錄器還包含了一個傳遞給函數的 arguments 對象(此對象存儲索引和參數的映射)和傳遞給函數的參數的 length。
        4.2.3 創建變量環境組件。
            4.2.3.1 它同樣是一個詞法環境,其環境記錄器持有變量聲明語句在執行上下文中創建的綁定關系。所以它有着上面定義的詞法環境的所有屬性。在 ES6 中,詞法環境組件和變量環境的一個不同就是前者被用來存儲函數聲明和變量(let 和 const)綁定,而后者只用來存儲 var 變量綁定。
        ExecutionContext = {
            ThisBinding = <this value>,
            LexicalEnvironment = { ... },
            VariableEnvironment = { ... },
        }

調用棧

0.前置說明:
    0.1 JavaScript 是一門單線程的語言,這意味着它只有一個調用棧,因此,它同一時間只能做一件事。
    0.2 內存堆:這是內存分配發生的地方.
    0.3 調用棧:這是你的代碼執行時的地方
1.定義:調用棧是解釋器(就像瀏覽器中的javascript解釋器)追蹤函數執行流的一種機制。當執行環境中調用了多個函數時,通過這種機制,我們能夠追蹤到哪個函數正在執行,執行的函數體中又調用了哪個函數。
    1.1 每調用一個函數,解釋器就會把該函數添加進調用棧並開始執行。
    1.2 正在調用棧中執行的函數還調用了其它函數,那么新函數也將會被添加進調用棧,一旦這個函數被調用,便會立即執行。
    1.3 當前函數執行完畢后,解釋器將其清出調用棧,繼續執行當前執行環境下的剩余的代碼。
    1.4 當分配的調用棧空間被占滿時,會引發“堆棧溢出”。
2.例子:
function Hello(){
        var a=1;
        hi();
        b=1;
    }
    function hi(){
        var c=1;
    }
    Hello();

  

  2.1 hello函數被調用,將Hello添加進調用棧表 
  2.2 執行Hello中的所有代碼
  2.3 直到執行hi,將hi添加進調用棧
2.4 執行hi的所有代碼,一直到執行完畢
2.5 刪除調用棧中的hi
2.6 繼續執行hello后面的代碼,執行到完畢
2.7 刪除調用棧中的hello


 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM