摘要:在初始化代碼時會先進入全局上下文中,每當一個函數被調用時就會為該函數創建一個執行上下文,每個函數都有自己的執行上下文。來看一段代碼這段代碼有個執行上下文全局上下文和,,屬于自己的執行上下文。
聊聊js的執行上下文
一,相關概念EC : 執行上下文二,執行上下文
ECS : 執行環境棧
VO : 變量對象
AO : 活動對象
scope chain :作用域鏈
javascript運行的代碼環境有三種:
全局代碼:代碼默認運行的環境,最先會進入到全局環境中 函數代碼:在函數的局部環境中運行的代碼 Eval代碼:在Eval()函數中運行的代碼
全局上下文是最外圍的一個執行環境,web瀏覽器中被認為是window對象。在初始化代碼時會先進入全局上下文中,每當一個函數被調用時就會為該函數創建一個執行上下文,每個函數都有自己的執行上下文。來看一段代碼:
function f1() { var f1Context = "f1 context"; function f2() { var f2Context = "f2 context"; function f3() { var f3Context = "f3 context"; console.log(f3Context); } f3(); console.log(f2Context); } f2(); console.log(f1Context); } f1();
這段代碼有4個執行上下文:全局上下文和f1(),f2(),f3()屬于自己的執行上下文。
全局上下文擁有變量f1(),f1()的上下文中有變量f1Context和f2(),f2()的上下文有變量f2Context和f3(),f3()上下文有變量f3Context。
在這我們了解下執行環境棧ECS,一段代碼所有的執行上下文都會被推入棧中等待被執行,因為js是單線程,任務都為同步任務的情況下某一時間只能執行一個任務,執行一段代碼首先會進入全局上下文中,并將其壓入ECS中,執行f1()會為其創建執行上下文壓入棧頂,f1()中有f2(),再為f2()創建f2()的執行上下文,依次,最終全局上下文被壓入到棧底,f3()的執行上下文在棧頂,函數執行完后,ECS就會彈出其上下文,f3()上下文彈出后,f2()上下文來到棧頂,開始執行f2(),依次,最后ECS中只剩下全局上下文,它等到應用程序退出,例如瀏覽器關閉時銷毀。
總結:(執行上下文就用EC替代)
1. 全局上下文壓入棧頂 2. 執行某一函數就為其創建一個EC,并壓入棧頂 3. 棧頂的函數執行完之后它的EC就會從ECS中彈出,并且變量對象(VO)隨之銷毀 4. 所有函數執行完之后ECS中只剩下全局上下文,在應用關閉時銷毀
大家再看一道道題:
function foo(i) { if(i == 3) { return; } foo(i+1); console.log(i); } foo(0);
大家明白執行上下文的進棧出棧就應該知道結果為什么是2,1,0
ECS棧頂為foo(3)的的上下文,直接return彈出后,棧頂變成foo(2)的上下文,執行foo(2),輸出2并彈出,執行foo(1),輸出1并彈出,執行foo(0),輸出0并彈出,關閉瀏覽器后全局EC彈出,所以結果為2,1,0
剛才提到VO,我們來了解什么是VO
三,VO/AO VO(變量對象)創建執行上下文時與之關聯的會有一個變量對象,該上下文中的所有變量和函數全都保存在這個對象中。AO(活動對象)
進入到一個執行上下文時,此執行上下文中的變量和函數都可以被訪問到,可以理解為被激活
談到了上下文的創建和執行,我們來看看EC建立的過程:
建立階段:(函數被調用,但是還未執行函數中的代碼) 1. 創建變量,參數,函數,arguments對象 2. 建立作用域鏈 3. 確定this的值 執行階段:變量賦值,函數引用,執行代碼
執行上下文為一個對象,包含VO,作用域鏈和this
executionContextObj = { variableObject: { /* 函數中的arguments對象, 參數, 內部的變量以及函數聲明 */ }, scopeChain: { /* variableObject 以及所有父執行上下文中的variableObject */ }, this: {} }
具體過程:
1. 找到當前上下文調用函數的代碼 2. 執行代碼之前,先創建執行上下文 3. 創建階段: 3-1. 創建變量對象(VO): 1. 創建arguments對象,檢查當前上下文的參數,建立該對象下的屬性和屬性值 2. 掃描上下文的函數申明: 1. 每掃描到一個函數什么就會在VO里面用函數名創建一個屬性, 為一個指針,指向該函數在內存中的地址 2. 如果函數名在VO中已經存在,對應的屬性值會被新的引用覆蓋 3. 掃描上下文的變量申明: 1. 每掃描到一個變量就會用變量名作為屬性名,其值初始化為undefined 2. 如果該變量名在VO中已經存在,則直接跳過繼續掃描 3-2. 初始化作用域鏈 3-3. 確定上下文中this的指向 4. 代碼執行階段 4-1. 執行函數體中的代碼,給VO中的變量賦值
看代碼理解:
function foo(i) { var a = "hello"; var b = function privateB() {}; function c() {} } foo(22);
調用foo(22)時創建上下文包括VO,作用域鏈,this值
以函數名作為屬性值,指向該函數在內存中的地址;變量名作為屬性名,其初始化值為undefined
注意:函數申明先于變量申明
fooExecutionContext = { variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer to function c(), a: undefined, b: undefined }, scopeChain: { ... }, this: { ... } }
創建階段結束后就會進入代碼執行階段,給VO中的變量賦值
fooExecutionContext = { variableObject: { arguments: { 0: 22, length: 1 }, i: 22, c: pointer to function c(), a: "hello", b: pointer to function privateB() }, scopeChain: { ... }, this: { ... } }四,變量提升
function foo() { console.log(f1); //f1() {} console.log(f2); //undefined var f1 = "hosting"; var f2 = function() {} function f1() {} } foo();
調用foo()時會創建VO,初始VO中變量值等有一系列的過程,所有變量初始化值為undefined,所以console.log(f2)的值為undefined。并且函數申明先于變量申明,所以console.log(f1)的值為f1()函數而不為hosting
五,總結1. 調用函數時會為其創建執行上下文,并壓入執行環境棧的棧頂,執行完畢 彈出,執行上下文被銷毀,隨之VO也被銷毀 2. EC創建階段分創建階段和代碼執行階段 3. 創建階段初始變量值為undefined,執行階段才為變量賦值 4. 函數申明先于變量申明
參考:
深入js之執行上下文
Execution Context
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/93939.html
摘要:深入系列第七篇,結合之前所講的四篇文章,以權威指南的為例,具體講解當函數執行的時候,執行上下文棧變量對象作用域鏈是如何變化的。前言在深入之執行上下文棧中講到,當代碼執行一段可執行代碼時,會創建對應的執行上下文。 JavaScript深入系列第七篇,結合之前所講的四篇文章,以權威指南的demo為例,具體講解當函數執行的時候,執行上下文棧、變量對象、作用域鏈是如何變化的。 前言 在《Jav...
摘要:下面,讓我們以一個函數的創建和激活兩個時期來講解作用域鏈是如何創建和變化的。這時候執行上下文的作用域鏈,我們命名為至此,作用域鏈創建完畢。 JavaScript深入系列第五篇,講述作用鏈的創建過程,最后結合著變量對象,執行上下文棧,讓我們一起捋一捋函數創建和執行的過程中到底發生了什么? 前言 在《JavaScript深入之執行上下文棧》中講到,當JavaScript代碼執行一段可執行代...
摘要:深入系列第八篇,介紹理論上的閉包和實踐上的閉包,以及從作用域鏈的角度解析經典的閉包題。定義對閉包的定義為閉包是指那些能夠訪問自由變量的函數。 JavaScript深入系列第八篇,介紹理論上的閉包和實踐上的閉包,以及從作用域鏈的角度解析經典的閉包題。 定義 MDN 對閉包的定義為: 閉包是指那些能夠訪問自由變量的函數。 那什么是自由變量呢? 自由變量是指在函數中使用的,但既不是函數參數也...
摘要:深入系列第三篇,講解執行上下文棧的是如何執行的,也回答了第二篇中的略難的思考題。 JavaScript深入系列第三篇,講解執行上下文棧的是如何執行的,也回答了第二篇中的略難的思考題。 順序執行? 如果要問到 JavaScript 代碼執行順序的話,想必寫過 JavaScript 的開發者都會有個直觀的印象,那就是順序執行,畢竟: var foo = function () { ...
摘要:本計劃一共期,每期重點攻克一個面試重難點,如果你還不了解本進階計劃,點擊查看前端進階的破冰之旅本期推薦文章深入之執行上下文棧和深入之變量對象,由于微信不能訪問外鏈,點擊閱讀原文就可以啦。 (關注福利,關注本公眾號回復[資料]領取優質前端視頻,包括Vue、React、Node源碼和實戰、面試指導) 本周正式開始前端進階的第一期,本周的主題是調用堆棧,今天是第二天。 本計劃一共28期,每期...
摘要:深入系列第四篇,具體講解執行上下文中的變量對象與活動對象。下一篇文章深入之作用域鏈本文相關鏈接深入之執行上下文棧深入系列深入系列目錄地址。 JavaScript深入系列第四篇,具體講解執行上下文中的變量對象與活動對象。全局上下文下的變量對象是什么?函數上下文下的活動對象是如何分析和執行的?還有兩個思考題幫你加深印象,快來看看吧! 前言 在上篇《JavaScript深入之執行上下文棧》中...
閱讀 713·2021-11-16 11:44
閱讀 3545·2019-08-26 12:13
閱讀 3241·2019-08-26 10:46
閱讀 2356·2019-08-23 12:37
閱讀 1187·2019-08-22 18:30
閱讀 2531·2019-08-22 17:30
閱讀 1840·2019-08-22 17:26
閱讀 2288·2019-08-22 16:20