激情久久久_欧美视频区_成人av免费_不卡视频一二三区_欧美精品在欧美一区二区少妇_欧美一区二区三区的

服務器之家:專注于服務器技術及軟件下載分享
分類導航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服務器之家 - 編程語言 - JavaScript - js教程 - JavaScript 中的執行上下文和執行棧實例講解

JavaScript 中的執行上下文和執行棧實例講解

2022-01-24 16:39沖冠為紅顏 js教程

這篇文章主要介紹了JavaScript 中的執行上下文和執行棧實例講解,文中實例講解的很清晰,有感興趣的同學可以研究下

JavaScript - 原理系列

? 在日常開發中,每當我們接手一個現有項目后,我們總喜歡先去看看別人寫的代碼。每當我們看到別人寫出很酷的代碼的時候,我們總會感慨!寫出這么優美而又簡潔的代碼的兄弟到底是怎么養成的呢?

? 我要怎樣才能達到和大佬一樣的水平呢!好了,廢話不多說,讓我們切入今天的主題。

一、執行上下文

? 簡而言之,【執行上下文】就是JavaScript 代碼被解析和執行時所在環境的抽象概念, 在JavaScript 中運行任何的代碼都是在它的執行上下文中運行。

? 在運行JavaScript代碼時,每當需要執行代碼時,執行代碼會先進入一個環境(瀏覽器、Node客戶端),這時就會為該環境創建一個執行上下文,它會在你運行代碼前做一些準備工作,如確定作用域,創建全局、局部變量對象等。

執行上下文的分類

  • 全局執行上下文:

? 這是默認的、最基礎的執行上下文。不在任何函數中的代碼都位于全局執行上下文中。

它做了兩件事:

  • 創建一個全局對象,在瀏覽器中這個全局對象就是 window 對象。

this 指針指向這個全局對象。一個程序中只能存在一個全局執行上下文。

  • 函數執行上下文:

? 每次調用函數時,都會為該函數創建一個新的執行上下文。每個函數都擁有自己的執行上下文,但是只有在函數被調用的時候才會被創建。一個程序中可以存在任意數量的函數執行上下文。每當一個新的執行上下文被創建,它都會按照特定的順序執行一系列步驟,具體過程將在本文后面討論。

  • Eval 函數執行上下文:

? 運行在 eval 函數中的代碼也獲得了自己的執行上下文,但由于 Javascript 開發人員不常用 eval 函數,所以在這里不再討論。

執行上下文的數量限制(堆棧溢出)

?執行上下文可存在多個,雖然沒有明確的數量限制,但如果超出棧分配的空間,會造成堆棧溢出。常見于遞歸調用,沒有終止條件造成死循環的場景。

下面是示例代碼:

?
1
2
3
4
5
6
// 遞歸調用自身
function foo() {
  foo();
}
foo();
// 報錯:Uncaught RangeError: Maximum call stack size exceeded

Tips:

? JS是“單線程”的,每次只執行一段代碼

二、執行棧

? JS中的執行棧,也就是在其它編程語言中所說的“調用棧”,是一種擁有 LIFO(后進先出)數據結構的棧,被用來存儲代碼運行時創建的所有執行上下文。

? 當 JavaScript 引擎第一次遇到你的腳本時,它會創建一個全局的執行上下文并且壓入當前執行棧。每當引擎遇到一個函數調用,它會為該函數創建一個新的執行上下文并壓入棧的頂部。

? 引擎會執行那些執行上下文位于棧頂的函數。當該函數執行結束時,執行上下文從棧中彈出,控制流程到達當前棧中的下一個上下文。

棧數據結構

JavaScript 中的執行上下文和執行棧實例講解

現在讓我們用一段代碼來理解執行棧

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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');

下圖是上面代碼的執行棧

JavaScript 中的執行上下文和執行棧實例講解

? 當上述代碼在瀏覽器加載時,瀏覽器的JavaScript 引擎會創建一個全局執行上下文并把它壓入當前執行棧。當遇到函數調用時,JavaScript 引擎為該函數創建一個新的執行上下文并把它壓入當前執行棧的頂部。

當從 first()函數內部調用 second()函數時,JavaScript 引擎為 second() 函數創建了一個新的執行上下文并把它壓入當前執行棧的頂部。當 second()函數執行完畢,它的執行上下文會從當前棧彈出,并且控制流程到達下一個執行上下文,即 first() 函數的執行上下文。

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

The Creation Phase

?在 JavaScript 代碼執行前,執行上下文將經歷創建階段。在創建階段會發生三件事:

  1. this 值的決定,即我們所熟知的 This 綁定。
  2. 創建詞法環境組件。
  3. 創建變量環境組件。

所以執行上下文在概念上表示如下:

?
1
2
3
4
5
ExecutionContext = {
 ThisBinding = <this value>,
 LexicalEnvironment = { ... },
 VariableEnvironment = { ... },
}

This 綁定:

? 在全局執行上下文中,this 的值指向全局對象。(在瀏覽器中,this引用 Window 對象)。

? 在函數執行上下文中,this 的值取決于該函數是如何被調用的。如果它被一個引用對象調用,那么 this 會被設置成那個對象,否則 this 的值被設置為全局對象或者undefined(在嚴格模式下)。例如:

?
1
2
3
4
5
6
7
8
9
10
let foo = {
 baz: function() {
 console.log(this);
 }
}
foo.baz();  // 'this' 引用 'foo', 因為 'baz' 被
       // 對象 'foo' 調用
let bar = foo.baz;
bar();    // 'this' 指向全局 window 對象,因為
       // 沒有指定引用對象

詞法環境

官方的 ES6 文檔把詞法環境定義為

? 詞法環境是一種規范類型,基于 ECMAScript 代碼的詞法嵌套結構來定義標識符和具體變量和函數的關聯。一個詞法環境由環境記錄器和一個可能的引用外部詞法環境的空值組成。

? 簡單來說詞法環境是一種持有標識符—變量映射的結構。(這里的標識符指的是變量/函數的名字,而變量是對實際對象[包含函數類型對象]或原始數據的引用)。

? 現在,在詞法環境的內部有兩個組件:(1) 環境記錄器和 (2) 一個外部環境的引用。

  1. 環境記錄器是存儲變量和函數聲明的實際位置。
  2. 外部環境的引用意味著它可以訪問其父級詞法環境(作用域)。

詞法環境有兩種類型:

  • 全局環境(在全局執行上下文中)是沒有外部環境引用的詞法環境。全局環境的外部環境引用是 null。它擁有內建的 Object/Array/等、在環境記錄器內的原型函數(關聯全局對象,比如 window 對象)還有任何用戶定義的全局變量,并且 this的值指向全局對象。
  • 在函數環境中,函數內部用戶定義的變量存儲在環境記錄器中。并且引用的外部環境可能是全局環境,或者任何包含此內部函數的外部函數。

環境記錄器也有兩種類型(如上!):

  1. 聲明式環境記錄器存儲變量、函數和參數。
  2. 對象環境記錄器用來定義出現在全局上下文中的變量和函數的關系。

簡而言之

  • 在全局環境中,環境記錄器是對象環境記錄器。
  • 在函數環境中,環境記錄器是聲明式環境記錄器。

注意

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GlobalExectionContext = {
 LexicalEnvironment: {
  EnvironmentRecord: {
   Type: "Object",
   // 在這里綁定標識符
  }
  outer: <null>
 }
}
 
FunctionExectionContext = {
 LexicalEnvironment: {
  EnvironmentRecord: {
   Type: "Declarative",
   // 在這里綁定標識符
  }
  outer: <Global or outer function environment reference>
 }
}

變量環境:

?它同樣是一個詞法環境,其環境記錄器持有變量聲明語句在執行上下文中創建的綁定關系。

如上所述,變量環境也是一個詞法環境,所以它有著上面定義的詞法環境的所有屬性。

? 在 ES6 中,詞法環境組件和變量環境的一個不同就是前者被用來存儲函數聲明和變量(letconst)綁定,而后者只用來存儲 var 變量綁定。

我們看點樣例代碼來理解上面的概念:

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

執行上下文看起來像這樣:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
GlobalExectionContext = {
 
 ThisBinding: <Global Object>,
 
 LexicalEnvironment: {
  EnvironmentRecord: {
   Type: "Object",
   // 在這里綁定標識符
   a: < uninitialized >,
   b: < uninitialized >,
   multiply: < func >
  }
  outer: <null>
 },
 
 VariableEnvironment: {
  EnvironmentRecord: {
   Type: "Object",
   // 在這里綁定標識符
   c: undefined,
  }
  outer: <null>
 }
}
 
FunctionExectionContext = {
 ThisBinding: <Global Object>,
 
 LexicalEnvironment: {
  EnvironmentRecord: {
   Type: "Declarative",
   // 在這里綁定標識符
   Arguments: {0: 20, 1: 30, length: 2},
  },
  outer: <GlobalLexicalEnvironment>
 },
 
VariableEnvironment: {
  EnvironmentRecord: {
   Type: "Declarative",
   // 在這里綁定標識符
   g: undefined
  },
  outer: <GlobalLexicalEnvironment>
 }
}

注意

?只有遇到調用函數 multiply 時,函數執行上下文才會被創建。

可能你已經注意到 letconst 定義的變量并沒有關聯任何值,但 var 定義的變量被設成了 undefined

?這是因為在創建階段時,引擎檢查代碼找出變量和函數聲明,雖然函數聲明完全存儲在環境中,但是變量最初設置為 undefinedvar 情況下),或者未初始化(letconst 情況下)。

?這就是為什么你可以在聲明之前訪問 var 定義的變量(雖然是 undefined),但是在聲明之前訪問 letconst 的變量會得到一個引用錯誤。

這就是我們說的變量聲明提升。

執行階段

?這是整篇文章中最簡單的部分。在此階段,完成對所有這些變量的分配,最后執行代碼。

注意

? 在執行階段,如果 JavaScript 引擎不能在源碼中聲明的實際位置找到 let 變量的值,它會被賦值為 undefined

結論

?我們已經討論過 JavaScript 程序內部是如何執行的。雖然要成為一名卓越的 JavaScript 開發者并不需要學會全部這些概念,但是如果對上面概念能有不錯的理解將有助于你更輕松,更深入地理解其他概念,如變量聲明提升,作用域和閉包。

參考文章:

https://juejin.cn/post/6844903682283143181

https://www.jianshu.com/p/6f8556b10379

https://juejin.cn/post/6844903704466833421

到此這篇關于JavaScript 中的執行上下文和執行棧實例講解的文章就介紹到這了,更多相關JavaScript 中的執行上下文和執行棧內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/abcode/p/14425614.html

延伸 · 閱讀

精彩推薦
  • js教程基于JavaScript實現簡單掃雷游戲

    基于JavaScript實現簡單掃雷游戲

    這篇文章主要介紹了基于JavaScript實現簡單掃雷游戲,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    北冰洋_WH4422021-12-23
  • js教程JavaScript實現原型封裝輪播圖

    JavaScript實現原型封裝輪播圖

    這篇文章主要為大家詳細介紹了JavaScript原型封裝輪播圖,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    zyhyoustrive12252021-12-21
  • js教程JS中多層次排序算法的實現代碼

    JS中多層次排序算法的實現代碼

    這篇文章主要給大家介紹了關于JS中多層次排序算法的實現代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需...

    桂花載酒少年游8192021-12-27
  • js教程如何在現代JavaScript中編寫異步任務

    如何在現代JavaScript中編寫異步任務

    這篇文章主要給大家介紹了關于如何在現代JavaScript中編寫異步任務的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考...

    瘋狂的技術宅11772022-01-12
  • js教程原生JavaScript實現購物車

    原生JavaScript實現購物車

    這篇文章主要為大家詳細介紹了原生JavaScript實現購物車,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下...

    棟棟很優秀啊4342021-12-29
  • js教程JS中錨點鏈接點擊平滑滾動并自由調整到頂部位置

    JS中錨點鏈接點擊平滑滾動并自由調整到頂部位置

    這篇文章主要介紹了JS中錨點鏈接點擊平滑滾動并自由調整到頂部位置,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的...

    是這樣的三月6382022-01-19
  • js教程JavaScript使用setTimeout實現倒計時效果

    JavaScript使用setTimeout實現倒計時效果

    這篇文章主要為大家詳細介紹了JavaScript使用setTimeout實現倒計時效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一...

    清水拌墨茶4902022-01-21
  • js教程關于uniApp editor微信滑動問題

    關于uniApp editor微信滑動問題

    這篇文章主要介紹了關于uniApp editor微信滑動問題,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下...

    辛夷不改年年色10142021-12-31
主站蜘蛛池模板: 毛片网站视频 | 久国产精品 | 欧美一级毛片欧美一级成人毛片 | 精品一区二区免费 | 中文字幕11 | 精国产品一区二区三区 | 久草成人在线 | 日韩视频在线不卡 | 88xx成人永久免费观看 | 欧美一级在线免费 | 久久99精品国产99久久6男男 | 久久草在线观看视频 | 国产精品99久久久久久久女警 | 精品国产一二区 | 久久精品操| 成人免费一区二区三区 | 99riav视频一区二区 | 91色一区二区三区 | 学霸趴下被打肿光屁股小说 | 国产亚洲精品久久久久久久久 | 国产高清美女一级毛片 | 国产羞羞视频在线免费观看 | 日本娇小videos高潮 | 久久九九热re6这里有精品 | 成年免费在线视频 | 免费毛片免费看 | www亚洲免费 | 日日草天天干 | 一级片在线免费 | 免费性爱视频 | 色羞羞| av噜噜噜噜 | 欧美成年人视频 | 99精品国产一区二区三区 | 高潮激情aaaaa免费看 | 在线播放视频一区二区 | 日本va在线观看 | 欧美成年人视频在线观看 | 成人在线精品视频 | 久久精品欧美一区 | 国产精品刺激对白麻豆99 |