摘要:本文就梳理有關(guān)執(zhí)行上下文也叫執(zhí)行環(huán)境的知識(shí)。全局代碼的執(zhí)行上下文棧可以表示為函數(shù)代碼當(dāng)執(zhí)行函數(shù)代碼時(shí),函數(shù)代碼上下文被壓入到執(zhí)行上下文棧中。
本文共 1090 字,讀完只需 4 分鐘概述
JavaScript 是函數(shù)式編程語言,作用域也是以函數(shù)為單位,那么,這些函數(shù)代碼塊是怎么樣的順序進(jìn)行的呢, JS 的可執(zhí)行代碼又分為 3 種,不同類型的代碼有不一樣的執(zhí)行環(huán)境。本文就梳理有關(guān) JS 執(zhí)行上下文(execution context),也叫執(zhí)行環(huán)境的知識(shí)。
活動(dòng)的執(zhí)行代碼的上下文在語言底層邏輯上構(gòu)成一個(gè)執(zhí)行上下文棧(excution context stack),我們知道,棧有兩個(gè)行為,壓入棧和彈出棧,并且有后進(jìn)先出的特點(diǎn), 最先進(jìn)入的項(xiàng)會(huì)在棧底最后彈出。
不同執(zhí)行環(huán)境的有其相應(yīng)的變量對(duì)象(Variable Object),某個(gè)執(zhí)行環(huán)境的所有可執(zhí)行代碼都執(zhí)行完畢后,該環(huán)境中的變量對(duì)象和函數(shù)定義會(huì)被清除。
函數(shù)代碼會(huì)在執(zhí)行完后清除變量占用的內(nèi)存,全局代碼則會(huì)在關(guān)閉環(huán)境,比如關(guān)閉瀏覽器后清除。
一、代碼類型JS 中可執(zhí)行的代碼可分為三種類型:
全局代碼
函數(shù)代碼
Eval 代碼
全局代碼
在 web 瀏覽器中,全局執(zhí)行環(huán)境是 window 對(duì)象,所有的全局變量和函數(shù)都是作為 window 的屬性和方法存在。
全局代碼的執(zhí)行上下文棧可以表示為:
ECStack = [ globalContext ]
函數(shù)代碼
當(dāng)執(zhí)行函數(shù)代碼時(shí),函數(shù)代碼上下文被壓入到執(zhí)行上下文棧中。函數(shù)代碼的執(zhí)行環(huán)境中,有自己的內(nèi)部的定義的變量和聲明。
function foo1() { var name1 = "a"; console.log(name1) } function foo2(){ var name2 = "b"; console.log(name2) } foo1(); foo2();
上面的執(zhí)行上下文可以表示為:
ECStack = [ globalContext ]; ECStack.push(functionContext); ECStack.pop(); ECStack.push( functionContext); ECStack.pop();
一個(gè)函數(shù),可能有多個(gè)執(zhí)行上下文,每個(gè)函數(shù)的調(diào)用都會(huì)產(chǎn)生新的上下文。
function foo() { ... } foo("a"); foo("b"); foo("c");
eval 代碼
eval 關(guān)鍵字接受一個(gè)字符串作為參數(shù),并將其作為書寫文字上下文的代碼。
function foo(str, a) { eval( str ); // 聲明一個(gè)新變量 console.log(a, b); } var b = 123; foo("var b = 456 ", 123) // 123, 456
以上代碼引用《你不知道的 JavaScript》的例子,eval 函數(shù)中的字符串,被當(dāng)做可執(zhí)行代碼,最后在 foo 函數(shù)中聲明了 b 變量,并覆蓋了 foo 函數(shù)外部的 b 變量。
eval 函數(shù)和 JS 的詞法作用域的行為,會(huì)造成變量環(huán)境和執(zhí)行上下文的混亂,盡量別使用它。
二、執(zhí)行上下文棧前面其實(shí)已經(jīng)提到很多關(guān)于執(zhí)行上下文棧的內(nèi)容,簡(jiǎn)而言之,JS 在遇到全局代碼,函數(shù)代碼,eval 代碼時(shí),會(huì)創(chuàng)建相應(yīng)的執(zhí)行上下文,不同的上下文,有其變量對(duì)象(variable object: VO)和作用域。
看這一段代碼:
function func3() { console.log("fun3") } function func2() { func3(); } function func1() { func2(); } func1();
以上代碼引用自冴羽的github,代碼順序用執(zhí)行上下文棧來表示就是:
ECStack.push(globalContext); // 調(diào)用 func1(), 進(jìn)入 func1 執(zhí)行環(huán)境 ECStack.push(functionContext); // func1中調(diào)用了 func2,進(jìn)入 func2 的執(zhí)行上下文 ECStack.push( functionContext); // func2 調(diào)用 func3, 進(jìn)入 func3 的執(zhí)行上下文 ECStack.push( functionContext); // func3 執(zhí)行完畢,退出 func3 執(zhí)行環(huán)境 ECStack.pop(); // func2 執(zhí)行完畢,退出 func2 執(zhí)行環(huán)境 ECStack.pop(); // func1 執(zhí)行完畢,退出 func1 執(zhí)行環(huán)境 ECStack.pop();
函數(shù)代碼執(zhí)行完畢后,執(zhí)行上下文棧底只剩下全局代碼上下文 globalContext, 直到關(guān)閉瀏覽器才會(huì)徹底清空?qǐng)?zhí)行上下文棧。
總結(jié)代碼在執(zhí)行時(shí)會(huì)創(chuàng)建由不同作用域構(gòu)成的作用域鏈,作用域鏈保證 JS 中的變量和函數(shù)都能夠有序地訪問和執(zhí)行。
如果是函數(shù)代碼,則將其 活動(dòng)對(duì)象 作為變量對(duì)象,活動(dòng)對(duì)象最開始時(shí)只包含一個(gè)對(duì)象,即 arguments 對(duì)象。
后面的文章會(huì)介紹變量對(duì)象(VO)和作用域鏈的具體內(nèi)容,敬請(qǐng)期待吧。
博客內(nèi)容源自個(gè)人公眾號(hào),專注分享原創(chuàng)文章,歡迎關(guān)注。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/98384.html
摘要:深入系列第七篇,結(jié)合之前所講的四篇文章,以權(quán)威指南的為例,具體講解當(dāng)函數(shù)執(zhí)行的時(shí)候,執(zhí)行上下文棧變量對(duì)象作用域鏈?zhǔn)侨绾巫兓摹G把栽谏钊胫畧?zhí)行上下文棧中講到,當(dāng)代碼執(zhí)行一段可執(zhí)行代碼時(shí),會(huì)創(chuàng)建對(duì)應(yīng)的執(zhí)行上下文。 JavaScript深入系列第七篇,結(jié)合之前所講的四篇文章,以權(quán)威指南的demo為例,具體講解當(dāng)函數(shù)執(zhí)行的時(shí)候,執(zhí)行上下文棧、變量對(duì)象、作用域鏈?zhǔn)侨绾巫兓摹?前言 在《Jav...
摘要:下面,讓我們以一個(gè)函數(shù)的創(chuàng)建和激活兩個(gè)時(shí)期來講解作用域鏈?zhǔn)侨绾蝿?chuàng)建和變化的。這時(shí)候執(zhí)行上下文的作用域鏈,我們命名為至此,作用域鏈創(chuàng)建完畢。 JavaScript深入系列第五篇,講述作用鏈的創(chuàng)建過程,最后結(jié)合著變量對(duì)象,執(zhí)行上下文棧,讓我們一起捋一捋函數(shù)創(chuàng)建和執(zhí)行的過程中到底發(fā)生了什么? 前言 在《JavaScript深入之執(zhí)行上下文棧》中講到,當(dāng)JavaScript代碼執(zhí)行一段可執(zhí)行代...
摘要:深入系列第八篇,介紹理論上的閉包和實(shí)踐上的閉包,以及從作用域鏈的角度解析經(jīng)典的閉包題。定義對(duì)閉包的定義為閉包是指那些能夠訪問自由變量的函數(shù)。 JavaScript深入系列第八篇,介紹理論上的閉包和實(shí)踐上的閉包,以及從作用域鏈的角度解析經(jīng)典的閉包題。 定義 MDN 對(duì)閉包的定義為: 閉包是指那些能夠訪問自由變量的函數(shù)。 那什么是自由變量呢? 自由變量是指在函數(shù)中使用的,但既不是函數(shù)參數(shù)也...
摘要:深入系列第三篇,講解執(zhí)行上下文棧的是如何執(zhí)行的,也回答了第二篇中的略難的思考題。 JavaScript深入系列第三篇,講解執(zhí)行上下文棧的是如何執(zhí)行的,也回答了第二篇中的略難的思考題。 順序執(zhí)行? 如果要問到 JavaScript 代碼執(zhí)行順序的話,想必寫過 JavaScript 的開發(fā)者都會(huì)有個(gè)直觀的印象,那就是順序執(zhí)行,畢竟: var foo = function () { ...
摘要:專題系列第十八篇,講解遞歸和尾遞歸定義程序調(diào)用自身的編程技巧稱為遞歸。然而非尾調(diào)用函數(shù),就會(huì)創(chuàng)建多個(gè)執(zhí)行上下文壓入執(zhí)行上下文棧。所以我們只用把階乘函數(shù)改造成一個(gè)尾遞歸形式,就可以避免創(chuàng)建那么多的執(zhí)行上下文。 JavaScript 專題系列第十八篇,講解遞歸和尾遞歸 定義 程序調(diào)用自身的編程技巧稱為遞歸(recursion)。 階乘 以階乘為例: function factorial(n...
摘要:本計(jì)劃一共期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)階計(jì)劃,點(diǎn)擊查看前端進(jìn)階的破冰之旅本期推薦文章深入之執(zhí)行上下文棧和深入之變量對(duì)象,由于微信不能訪問外鏈,點(diǎn)擊閱讀原文就可以啦。 (關(guān)注福利,關(guān)注本公眾號(hào)回復(fù)[資料]領(lǐng)取優(yōu)質(zhì)前端視頻,包括Vue、React、Node源碼和實(shí)戰(zhàn)、面試指導(dǎo)) 本周正式開始前端進(jìn)階的第一期,本周的主題是調(diào)用堆棧,今天是第二天。 本計(jì)劃一共28期,每期...
閱讀 1349·2021-09-28 09:43
閱讀 4117·2021-09-04 16:41
閱讀 1918·2019-08-30 15:44
閱讀 3729·2019-08-30 15:43
閱讀 776·2019-08-30 14:21
閱讀 2037·2019-08-30 11:00
閱讀 3320·2019-08-29 16:20
閱讀 1923·2019-08-29 14:21