一篇文章弄懂javascript中的執(zhí)行棧與執(zhí)行上下文
前言
作為一個前端開發(fā)人員,弄清楚JavaScript的執(zhí)行上下文有助于我們理解js中一些晦澀的概念,比如閉包,作用域,變量提升等等。
執(zhí)行棧
執(zhí)行棧用于存儲代碼執(zhí)行期間創(chuàng)建的所有執(zhí)行上下文。具有FILO接口,也被稱為調(diào)用棧。
當(dāng)JavaScript代碼被運行的時候,會創(chuàng)建一個全局上下文,并push到當(dāng)前執(zhí)行棧。之后當(dāng)發(fā)生函數(shù)調(diào)用的時候,引擎會為函數(shù)創(chuàng)建一個函數(shù)執(zhí)行上下文并push到棧頂。引擎會先執(zhí)行調(diào)用棧頂部的函數(shù),當(dāng)函數(shù)執(zhí)行完成后,當(dāng)前函數(shù)的執(zhí)行上下文會被移除當(dāng)前執(zhí)行棧。并移動到下一個上下文。
let a = 'Hello' function first() { console.log('Inside first function') second() console.log('Again inside first function') } function second() { console.log('Inside second function') } first() console.log('Inside Global execution context')
執(zhí)行上下文
我們可以理解為執(zhí)行上下文是js代碼被解析和執(zhí)行時所在環(huán)境的抽象概念。
JavaScript的執(zhí)行上下文分為三種類型:
- 全局執(zhí)行上下文:最基本的上下文,只有一個。創(chuàng)建了一個全局對象(eg:瀏覽器中的window),將this指向全局對象。
- 函數(shù)執(zhí)行上下文:每次調(diào)用函數(shù)都會創(chuàng)建一個函數(shù)上下文。函數(shù)上下文可以存在無數(shù)個。
- Eval函數(shù)執(zhí)行上下文:運行在eval函數(shù)中的代碼會有一個自己的執(zhí)行上下文(本文不討論)。
執(zhí)行上下文的創(chuàng)建
創(chuàng)建執(zhí)行上下文又可以分為兩個階段: 1. 創(chuàng)建階段 2. 執(zhí)行階段
創(chuàng)建階段
在創(chuàng)建階段,JavaScript引擎會創(chuàng)建LexicalEnvironment(詞法環(huán)境)組件,VariableEnvironment(變量環(huán)境)組件以及this綁定(在全局上下文中,this指向全局對象。在函數(shù)執(zhí)行上下文中,this取決與函數(shù)在哪里被調(diào)用。)
Lexical Environment(詞法環(huán)境)
我們可以理解為詞法環(huán)境是一種包含標(biāo)識符(變量/函數(shù)的名稱)和變量(實際對象:函數(shù)/原始值/數(shù)組對象等)映射的數(shù)據(jù)結(jié)構(gòu)。
每個詞法環(huán)境由兩部分組成:Environment Record環(huán)境記錄(存儲變量和函數(shù)聲明的實際位置)和對外部環(huán)境的引用(可以訪問其外部詞法環(huán)境)
環(huán)境記錄分為兩種:
- Declarative environment record(聲明性環(huán)境記錄): 存儲變量和函數(shù)聲明。
- Object environment record(對象環(huán)境記錄):全局詞法環(huán)境。除了變量和函數(shù)聲明外,對象環(huán)境記錄還存儲全局綁定對象。
tips:對于函數(shù)執(zhí)行上下文來說,環(huán)境記錄還包含了一個arguments對象,包含了傳遞給函數(shù)的索引和參數(shù)之間的映射以及函數(shù)參數(shù)的數(shù)量。
例如:
function foo(a, b) { var c = a + b } // argument object Arguments: {0: 2, 1: 3, length: 2}
VariableEnvironment(變量環(huán)境)
變量環(huán)境也是一個詞匯環(huán)境,因此它具有上面定義的詞法環(huán)境的所有屬性和組件。在ES6中,LexicalEnvironment組件和VariableEnvironment組件之間的一個區(qū)別是前者用于存儲函數(shù)聲明和變量let和const綁定,而后者僅用于存儲變量var綁定。
執(zhí)行階段
在執(zhí)行階段,會完成對所有變量的分配,代碼也會被最終執(zhí)行。執(zhí)行上下文的代碼會被分成兩個階段:
- 進入執(zhí)行上下文
- 代碼執(zhí)行
let a = 20 const b = 30 var c function multiply(e, f) { var g = 20 return e * f * g } c = multiply(20, 30)
當(dāng)上面的代碼被執(zhí)行的時候,JavaScript引擎會創(chuàng)建一個全局上下文來執(zhí)行全局代碼。
此時的全局上下文:
GlobalExectionContext = { // 全局上下文 LexicalEnvironment: { // 詞法環(huán)境 EnvironmentRecord: { // 環(huán)境記錄 type: 'Object', // 類型 // 標(biāo)識符 a: <uninitialized>, b: <uninitialized>, multiply: <func> }, outer: <null>, // 對外部環(huán)境引用 }, VariableEnvironment: { // 變量環(huán)境 EnvironmentRecord: { // 環(huán)境記錄 type: 'Object', c: undefined }, outer: <null>, // 對外部環(huán)境引用 }, ThisBinding: <Global Object> // this綁定 }
在執(zhí)行階段,變量會被賦值,此時的全局上下文變成:
GlobalExectionContext = { LexicalEnvironment: { EnvironmentRecord: { type: 'Object', a: 20, b: 30, multiply: <func> }, outer: <null> }, VariableEnvironment: { EnvironmentRecord: { type: 'Object', c: undefined } outer: <null> }, ThisBinding: <Global Object> }
當(dāng)JavaScript引擎遇到函數(shù)multiply(20, 30)的時候,會創(chuàng)建一個新的函數(shù)上下文:
FunctionExectionContext = { LexicalEnvironment: { EnvironmentRecord: { type: 'Declarative', arguments: { 0: 20, 1: 30, length: 2 } }, outer: <GlobalLexicalEnvironment> }, VariableEnvironment: { EnvironmentRecord: { Type: "Declarative", g: undefined }, outer: <GlobalLexicalEnvironment>, }, thisBinding: <Global Object or undefined> }
之后執(zhí)行上下文會進入執(zhí)行階段, 此時的函數(shù)上下文:
FunctionExectionContext = { LexicalEnvironment: { EnvironmentRecord: { type: 'Declarative', arguments: { 0: 20, 1: 30, length: 2 } }, outer: <GlobalLexicalEnvironment> }, VariableEnvironment: { EnvironmentRecord: { Type: "Declarative", g: 20 }, outer: <GlobalLexicalEnvironment>, }, thisBinding: <Global Object or undefined> }
當(dāng)函數(shù)執(zhí)行完畢后。會返回一個值并賦值給變量c,全局上下文被更新。更新完成后,代碼執(zhí)行完畢,程序結(jié)束。
參考文章
Understanding Execution Context and Execution Stack in Javascript
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
相關(guān)文章
微信小程序?qū)崿F(xiàn)自定義動畫彈框/提示框的方法實例
這篇文章主要給大家介紹了關(guān)于微信小程序?qū)崿F(xiàn)自定義動畫彈框/提示框的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11CSS+Table圖文混排中實現(xiàn)文本自適應(yīng)圖片寬度(超簡單+跨所有瀏覽器)
最近在為學(xué)樂網(wǎng)開發(fā)圖片顯示功能時遇到一個問題:在一個table中有兩行,上邊顯示圖片(大小隨機),下邊顯示對圖片的相關(guān)說明(文字長度隨機)2009-02-02DataGear開發(fā)基于three.js的3D數(shù)據(jù)可視化看板的詳細(xì)代碼
DataGear?支持采用原生的HTML、JavaScript、CSS制作數(shù)據(jù)可視化看板,也支持導(dǎo)入由npm、vite等前端工具構(gòu)建的前端程序包,這篇文章主要介紹了DataGear制作基于three.js的3D數(shù)據(jù)可視化看板,需要的朋友可以參考下2024-02-02JavaScript中break、continue和return的用法區(qū)別實例分析
這篇文章主要介紹了JavaScript中break、continue和return的用法區(qū)別,結(jié)合實例形式詳細(xì)對比分析了JavaScript中break、continue和return的基本功能、使用方法、區(qū)別與操作注意事項,需要的朋友可以參考下2020-03-03