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