日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

javascript的执行上下文

發(fā)布時間:2023/12/31 javascript 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 javascript的执行上下文 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

https://juejin.im/post/5ba32171f265da0ab719a6d7
https://juejin.im/entry/599867b2518825241e220eaa
我只是搬運工,看了他們的文章后深有啟發(fā),于是把他們的精華匯總?cè)缓蠹尤胱约旱睦斫庹砹诉@一篇文章。

這是一個非常抽象的概念,你無需徹底的弄明白它的意思,你只需要明白它做了什么。

在充分理解他做了什么之前還是要了解一下它到底是什么

Execution Context(執(zhí)行上下文)是 ECMA-262 標準中定義的一個抽象概念,用于同 Executable
Code(可執(zhí)行代碼)進行區(qū)分。

1:什么是執(zhí)行代碼----Executable Code

合法的,可以被解釋器解析執(zhí)行的代碼。

分為三類

  • Global Code:全局代碼
  • Function Code:函數(shù)體內(nèi)的代碼
  • Eval Code:使用 eval() 函數(shù)執(zhí)行的代碼

2:什么是執(zhí)行上下文----Execution Context

執(zhí)行上下文 是 ES 用來跟蹤代碼運行狀態(tài)和相關(guān)資源集合的特殊機制。它決定了執(zhí)行代碼執(zhí)行的過程中可以訪問的數(shù)據(jù)。

每當 Javascript 代碼在運行的時候,它都是在執(zhí)行上下文中運行。

分為三類

  • Global Execution Context:全局執(zhí)行上下文

這是默認或者說基礎(chǔ)的上下文,任何不在函數(shù)內(nèi)部的代碼都在全局上下文中執(zhí)行。它會執(zhí)行兩件事:創(chuàng)建一個全局的 window
對象(瀏覽器的情況下),并且設(shè)置 this 的值等于這個全局對象。一個程序中只會有一個全局執(zhí)行上下文。

  • Function Execution Context:函數(shù)執(zhí)行上下文

每當一個函數(shù)被調(diào)用時, 都會為該函數(shù)創(chuàng)建一個新的上下文。每個函數(shù)被調(diào)用時都有它自己的執(zhí)行上下文。函數(shù)上下文可以有任意多個。每當一個新的執(zhí)行上下文被創(chuàng)建,它會按定義的順序(將在后文討論)執(zhí)行一系列步驟。

  • Eval Execution Context:eval() 函數(shù)執(zhí)行上下文

由于 JavaScript 開發(fā)者并不經(jīng)常使用 eval,所以在這里我不會討論它。

3:執(zhí)行上下文的基本工作方式

先理解兩個名詞:執(zhí)行上下文棧(Execution Context Stack)、運行執(zhí)行上下文(Running Execution Context)

執(zhí)行上下文棧( Execution Context Stack ):用來保存所有執(zhí)行上下文的棧,是一種擁有 LIFO(后進先出)數(shù)據(jù)結(jié)構(gòu)的棧。 當 JavaScript 引擎第一次遇到你的腳本時,它會創(chuàng)建一個全局的執(zhí)行上下文并且壓入當前執(zhí)行棧。每當引擎遇到一個函數(shù)調(diào)用,它會為該函數(shù)創(chuàng)建一個新的執(zhí)行上下文并壓入棧的頂部。引擎會執(zhí)行那些執(zhí)行上下文位于棧頂?shù)暮瘮?shù)。當該函數(shù)執(zhí)行結(jié)束時,執(zhí)行上下文從棧中彈出,控制流程到達當前棧中的下一個上下文。

運行執(zhí)行上下文( Running Execution Context ):正在使用的執(zhí)行上下文。在任意時間,最多只能有一
個正在運行代碼的執(zhí)行上下文。

4:基本工作方式

運行執(zhí)行上下文總是在執(zhí)行上下文棧的頂部,全局執(zhí)行上下文總在執(zhí)行上下文棧的底部。無論什么時候,只要控制權(quán)從與當前運行執(zhí)行上下文相關(guān)的可執(zhí)行代碼上切換到另一部分與當前運行執(zhí)行上下文不相關(guān)的可執(zhí)行代碼上,一個新的執(zhí)行上下文就會被創(chuàng)建,新創(chuàng)建的執(zhí)行上下文會被放在當前的運行執(zhí)行上下文的上面,成為新的運行執(zhí)行上下文。

5:具體工作流程

如前言中提到的,ES 標準中并沒有從技術(shù)實現(xiàn)的角度定義執(zhí)行上下文準確類型和結(jié)構(gòu),為了更方便地解釋
執(zhí)行代碼和執(zhí)行上下文之間的關(guān)系,暫且用數(shù)組表示執(zhí)行上下文棧,然后用偽代碼來操作執(zhí)行上下文棧:

DCStack = [] // 執(zhí)行上下文棧

<1:開始執(zhí)行代碼:全局執(zhí)行代碼與全局執(zhí)行上下文

解析器在解析執(zhí)行代碼時首先執(zhí)行全局代碼,為其創(chuàng)建對應(yīng)的執(zhí)行上下文,全局上下文被壓入執(zhí)行上下文棧

ECStack = [globalContext // 全局執(zhí)行上下文 ]

<2:開始執(zhí)行函數(shù):函數(shù)代碼與函數(shù)執(zhí)行上下文

注意:函數(shù)代碼中不包括內(nèi)部函數(shù)的代碼

運行下面的函數(shù)(function foo(bar) {if (bar) {return}foo(true); })()我們用偽代碼還原一下執(zhí)行棧中發(fā)生了什么??// 第一次調(diào)用 fooECStack = [<foo> functionContext,globalContext ]// 第二次調(diào)用 fooECStack = [<foo> functionContext – recursively(遞歸),<foo> functionContext,globalContext ]

我們看一個實際的例子

let a = 'Hello World!';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í)行這段代碼,解析器解析到了這段代碼,于是先創(chuàng)建了一個全局上下文,并把全局上下文壓入執(zhí)行棧

ECStack = [Global Context ]

然后解析器檢測到了 first(),開始調(diào)用first函數(shù),于是創(chuàng)建了一個first函數(shù)上下文,并把這個函數(shù)向下文壓入到執(zhí)行棧的頂部(一般執(zhí)行棧的頂部都是正在運行的上下文,現(xiàn)在正在調(diào)用first函數(shù),所以頂部就是他的上下文)

ECSstack= [First Function Context-----(頂部是正在執(zhí)行的上下文)Global Context ]

在first() 函數(shù)內(nèi)部又調(diào)用了second()函數(shù),于是JavaScript 引擎為second()函數(shù)創(chuàng)建了一個屬于他的執(zhí)行上下文,并把它壓入執(zhí)行棧的最頂部。(因為現(xiàn)在執(zhí)行second()函數(shù),所以他的執(zhí)行上下文就在最頂部,因為first()函數(shù)沒有執(zhí)行完所以他的執(zhí)行上下文依然在執(zhí)行棧的隊列中)

ECSstack = [Cecond Function Context-----(頂部是正在執(zhí)行的上下文)First Function ContextGlobal Context ]

執(zhí)行完second()函數(shù)之后,它的執(zhí)行上下文會自動從執(zhí)行棧彈出,并且控制流程執(zhí)行下一個執(zhí)行上下文,即 first() 函數(shù)的執(zhí)行上下文。

ECSstack= [First Function Context-----(頂部是正在執(zhí)行的上下文)Global Context ]

當 first() 執(zhí)行完畢,它的執(zhí)行上下文自動從棧彈出,控制流程按順序到達全局執(zhí)行上下文。一旦所有代碼執(zhí)行完畢,JavaScript 引擎從當前棧中移除全局執(zhí)行上下文。

ECStack = [Global Context ]

6:JavaScript 引擎是怎么創(chuàng)建執(zhí)行上下文?

創(chuàng)建執(zhí)行上下文有兩個階段:1>:創(chuàng)建階段 和 2>:執(zhí)行階段。

1>:創(chuàng)建階段–(The Creation Phase)

在創(chuàng)建階段會發(fā)生三件事

ExecutionContext = {ThisBinding = <this value>, // this綁定LexicalEnvironment = { ... }, // 詞法環(huán)境VariableEnvironment = { ... }, // 變量環(huán)境}
  • This 綁定。

在全局執(zhí)行上下文中,this 的值指向全局對象。(在瀏覽器中,this引用 Window 對象)。在函數(shù)執(zhí)行上下文中,this 的值取決于該函數(shù)是如何被調(diào)用的。如果它被一個引用對象調(diào)用,那么 this 會被設(shè)置成那個對象,否則 this 的值被設(shè)置為全局對象或者 undefined(在嚴格模式下)。例如:

let foo = {baz: function() {console.log(this);} } foo.baz(); // 'this' 引用 'foo', 因為 'baz' 被對象 'foo' 調(diào)用let bar = foo.baz;bar(); // 'this' 指向全局 window 對象,因為沒有指定引用對象
  • 創(chuàng)建詞法環(huán)境組件。

詞法環(huán)境是一種規(guī)范類型,基于 ECMAScript 代碼的詞法嵌套結(jié)構(gòu)來定義標識符和具體變量和函數(shù)的關(guān)聯(lián)。一個詞法環(huán)境由環(huán)境記錄器和一個可能的引用外部詞法環(huán)境的空值組成。

有點沒明白

簡單來說詞法環(huán)境是一種定義標識符以及變量的嵌套結(jié)構(gòu)。(這里的標識符指的是變量/函數(shù)的名字,而變量是對實際對象[包含函數(shù)類型對象]或原始數(shù)據(jù)的引用)。

在詞法環(huán)境的內(nèi)部有兩個部件組成:

1:環(huán)境記錄器:是存儲變量和函數(shù)聲明的實際位置。

:2: 外部環(huán)境的引用:意味著它可以訪問其父級詞法環(huán)境(作用域)。

詞法環(huán)境有兩種類型:

1:全局環(huán)境:(在全局執(zhí)行上下文中)是沒有外部環(huán)境引用的詞法環(huán)境。全局環(huán)境的外部環(huán)境引用是 null。它擁有內(nèi)建的
Object/Array/等、在環(huán)境記錄器內(nèi)的原型函數(shù)(關(guān)聯(lián)全局對象,比如 window 對象)還有任何用戶定義的全局變量,并且
this的值指向全局對象。

2:函數(shù)環(huán)境:函數(shù)內(nèi)部用戶定義的變量存儲在環(huán)境記錄器中。并且引用的外部環(huán)境可能是全局環(huán)境,或者任何包含此內(nèi)部函數(shù)的外部函數(shù)。

環(huán)境記錄器也有兩種類型:

1:聲明式環(huán)境記錄器存儲變量、函數(shù)和參數(shù)。

2:對象環(huán)境記錄器用來定義出現(xiàn)在全局上下文中的變量和函數(shù)的關(guān)系。

簡而言之,

環(huán)境記錄器全局環(huán)境中,環(huán)境記錄器是對象環(huán)境記錄器。 在函數(shù)環(huán)境中,環(huán)境記錄器是聲明式環(huán)境記錄器。

注意

函數(shù)環(huán)境,聲明式環(huán)境記錄器還包含了一個傳遞給函數(shù)的 arguments 對象(此對象存儲索引和參數(shù)的映射和傳遞給函數(shù)的參數(shù)的length)

抽象地講,詞法環(huán)境在偽代碼中看起來像這樣:

GlobalExectionContext = { // 全局執(zhí)行上下文LexicalEnvironment: { // 詞法環(huán)境組件EnvironmentRecord: { // 環(huán)境記錄器 ---對象環(huán)境記錄器Type: "Object",// 在這里綁定標識符}outer: <null> // 外部環(huán)境引用, 是沒有外部環(huán)境引用的詞法環(huán)境。全局環(huán)境的外部環(huán)境引用是 null。} }FunctionExectionContext = { // 函數(shù)執(zhí)行上下文 LexicalEnvironment: { // 詞法環(huán)境組件EnvironmentRecord: { // 環(huán)境記錄器 ---聲明式環(huán)境記錄器Type: "Declarative",// 在這里綁定標識符}outer: <Global or outer function environment reference> //外部環(huán)境引用 函數(shù)內(nèi)部用戶定義的變量存儲在環(huán)境記錄器中。并且引用的外部環(huán)境可能是全局環(huán)境,或者任何包含此內(nèi)部函數(shù)的外部函數(shù)。} }
  • 創(chuàng)建變量環(huán)境組件。

變量環(huán)境也是一個詞法環(huán)境。所以它有著上面定義的詞法環(huán)境的所有屬性,其環(huán)境記錄器持有變量聲明語句在執(zhí)行上下文中創(chuàng)建的綁定關(guān)系。

在 ES6 中,詞法環(huán)境組件變量環(huán)境組件的一個不同就是前者被用來存儲函數(shù)聲明和變量(let 和 const)綁定,而后者只用來存儲 var 變量綁定

來個栗子

let a = 20; const b = 30; var c;function multiply(e, f) {var g = 20;return e * f * g; }c = multiply(20, 30);

執(zhí)行上下文用偽函數(shù)這么表示

// 全局執(zhí)行上下文 GlobalExectionContext = { 1:ThisBinding: <Global Object>, //this綁定2: LexicalEnvironment: { // 詞法環(huán)境 --全局的詞法環(huán)境EnvironmentRecord: { //環(huán)境記錄器Type: "Object", // 在這里綁定標識符a: < uninitialized >, // 變量a的綁定(let)b: < uninitialized >, // 變量b 的綁定(const)multiply: < func > // 函數(shù)聲明}outer: <null> // 外部環(huán)境的引用nul},3: VariableEnvironment: { // 變量環(huán)境 --全局的詞法環(huán)境EnvironmentRecord: { //環(huán)境記錄器Type: "Object",// 在這里綁定標識符c: undefined, // 變量c 的綁定(var)}outer: <null> // 外部環(huán)境的引用nul} }// 函數(shù)的執(zhí)行上下文-----(只有遇到調(diào)用函數(shù) multiply 時,函數(shù)執(zhí)行上下文才會被創(chuàng)建) FunctionExectionContext = { 1:ThisBinding: <Global Object>, // this 綁定 2:LexicalEnvironment: { //詞法環(huán)境 --函數(shù)的詞法環(huán)境EnvironmentRecord: { // 環(huán)境記錄器Type: "Declarative",// 在這里綁定標識符Arguments: {0: 20, 1: 30, length: 2}, // 聲明式環(huán)境記錄器還包含了一個傳遞給函數(shù)的 arguments 對象(此對象存儲函數(shù)參數(shù)鍵值對和傳遞給函數(shù)的參數(shù)的length)。},outer: <GlobalLexicalEnvironment> // 外部環(huán)境的引用是全局環(huán)境},3:VariableEnvironment: { //變量環(huán)境EnvironmentRecord: { // 環(huán)境記錄器Type: "Declarative",// 在這里綁定標識符g: undefined // 變量g的綁定(var)},outer: <GlobalLexicalEnvironment> // 外部環(huán)境的引用是全局環(huán)境} }

可能你已經(jīng)注意到 let 和 const 定義的變量并沒有關(guān)聯(lián)任何值,但 var 定義的變量被設(shè)成了 undefined。
這是因為在創(chuàng)建階段時,引擎檢查代碼找出變量和函數(shù)聲明,雖然函數(shù)聲明完全存儲在環(huán)境中,但是變量最初設(shè)置為 undefined(var
情況下),或者未初始化(let 和 const 情況下)。 這就是為什么你可以在聲明之前訪問 var 定義的變量(雖然是
undefined),但是在聲明之前訪問 let 和 const 的變量會得到一個引用錯誤。 這就是我們說的變量聲明提升。

總結(jié)

以上是生活随笔為你收集整理的javascript的执行上下文的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。