摘要:理解執(zhí)行上下文和執(zhí)行堆棧對(duì)于理解的其它概念如提升,范圍和閉包至關(guān)重要。正確地理解執(zhí)行上下文和執(zhí)行堆棧將幫助你更好地使用開(kāi)發(fā)應(yīng)用。引擎執(zhí)行位于執(zhí)行堆棧頂部的方法。當(dāng)調(diào)用時(shí),為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文,并且把它推入到當(dāng)前執(zhí)行堆棧。
原文
如果你是或者你想要成為一名js開(kāi)發(fā)者,那么你必須了解js程序內(nèi)部的運(yùn)作。理解執(zhí)行上下文和執(zhí)行堆棧對(duì)于理解js的其它概念(如提升,范圍和閉包)至關(guān)重要。
正確地理解執(zhí)行上下文和執(zhí)行堆棧將幫助你更好地使用js開(kāi)發(fā)應(yīng)用。
廢話(huà)少說(shuō),讓我們開(kāi)始吧:)
簡(jiǎn)單來(lái)說(shuō),執(zhí)行上下文是預(yù)估和執(zhí)行當(dāng)前環(huán)境下js代碼的抽象概念。每當(dāng)在js中運(yùn)行代碼時(shí),它都在執(zhí)行上下文中運(yùn)行。
(譯者:emmm,就是執(zhí)行上下文包含了追蹤當(dāng)前正在執(zhí)行的代碼的全部狀態(tài)。)
執(zhí)行上下文的類(lèi)型在js中有三種執(zhí)行類(lèi)型
全局執(zhí)行上下文——這是默認(rèn)或者說(shuō)基礎(chǔ)執(zhí)行上下文。函數(shù)外的代碼就處于全局執(zhí)行上下文中。它做了兩件事:它創(chuàng)建了window對(duì)象(在瀏覽器環(huán)境下),也就是全局對(duì)象。并把this指向全局對(duì)象。在程序里面只能有一個(gè)全局上下文。
函數(shù)執(zhí)行上下文——每次函數(shù)被調(diào)用,都會(huì)為這個(gè)函數(shù)創(chuàng)建一個(gè)新的上下文。每個(gè)函數(shù)都有自己的上下文,但是只有被調(diào)用的時(shí)候才會(huì)被創(chuàng)建??梢杂泻芏鄠€(gè)函數(shù)執(zhí)行上下文。每當(dāng)創(chuàng)建一個(gè)新的函數(shù)執(zhí)行上下文,js引擎都會(huì)按照定義好的順序執(zhí)行一系列的步驟,我將會(huì)在下文中討論。
Eval 函數(shù)的執(zhí)行上下文——eval函數(shù)執(zhí)行的時(shí)候也會(huì)為它里面的代碼創(chuàng)建上下文,但是這個(gè)方法用的少,在本文略過(guò)。
執(zhí)行堆棧執(zhí)行堆棧在其它語(yǔ)言中被稱(chēng)為“調(diào)用?!?,是一種先進(jìn)后出的一種數(shù)據(jù)結(jié)構(gòu),在代碼執(zhí)行期間被用于存儲(chǔ)所有的執(zhí)行上下文。
當(dāng)js引擎開(kāi)始解析js代碼時(shí),會(huì)先創(chuàng)建全局執(zhí)行上下文并且放在當(dāng)前執(zhí)行堆棧中。每當(dāng)引擎遇到函數(shù)調(diào)用的代碼時(shí),都會(huì)創(chuàng)建該函數(shù)的上下文并推入當(dāng)前執(zhí)行堆棧中。
引擎執(zhí)行位于執(zhí)行堆棧頂部的方法。當(dāng)方法執(zhí)行完畢,執(zhí)行堆棧pop掉最頂部的上下文,接著引擎繼續(xù)執(zhí)行堆棧頂部的方法。
用代碼示范一下:
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");
當(dāng)上述代碼在瀏覽器內(nèi)加載,js引擎就會(huì)創(chuàng)建一個(gè)全局執(zhí)行上下文并且把它推入當(dāng)前執(zhí)行堆棧中。當(dāng)調(diào)用first()時(shí),js為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文,并且把它推入到當(dāng)前執(zhí)行堆棧。
當(dāng)second()方法被first()調(diào)用,js引擎為該方法創(chuàng)建一個(gè)新的執(zhí)行上下文并把它推入當(dāng)前執(zhí)行堆棧。當(dāng)second()執(zhí)行完畢,這個(gè)方法的上下文就被執(zhí)行堆棧推出,并且執(zhí)行下一個(gè)執(zhí)行上下文,也就是first()。
當(dāng)first()執(zhí)行完畢,重復(fù)以上步驟。一旦執(zhí)行了所有代碼,JavaScript引擎就會(huì)從當(dāng)前堆棧中刪除全局執(zhí)行上下文。
執(zhí)行上下文如何創(chuàng)建?直到現(xiàn)在,我們已經(jīng)知道js引擎如何管理執(zhí)行上下文的了,現(xiàn)在讓我們了解下執(zhí)行上下文如何被js創(chuàng)建的。
創(chuàng)建執(zhí)行上下文有兩個(gè)階段:1)創(chuàng)建階段,2)執(zhí)行階段(譯者:???懵逼臉)。
創(chuàng)建階段在執(zhí)行任何JavaScript代碼之前,執(zhí)行上下文將經(jīng)歷創(chuàng)建階段。在創(chuàng)建階段會(huì)發(fā)生三件事:
this綁定
詞法環(huán)境(LexicalEnvironment)創(chuàng)建
變量環(huán)境(VariableEnvironment)創(chuàng)建
(譯者:VariableEnvironment和LexicalEnvironment譯者也是第一次聽(tīng)到,慚愧,大學(xué)沒(méi)學(xué)過(guò)編譯原理,在js中還有個(gè)this綁定,似乎是js特有)
因此,執(zhí)行上下文在概念上可以這樣表示,如下:
ExecutionContext = { ThisBinding =this綁定, LexicalEnvironment = { ... }, VariableEnvironment = { ... }, }
在全局執(zhí)行上下文中,this值指向全局對(duì)象(在瀏覽器內(nèi)是window對(duì)象)。
在函數(shù)執(zhí)行上下文中,this的值取決于函數(shù)的調(diào)用的時(shí)候的情況。如果它由對(duì)象引用調(diào)用,this值就是該對(duì)象,否則this值指向全局或者為undefined(在嚴(yán)格模式下)。
例如:
let person = { name: "peter", birthYear: 1994, calcAge: function() { console.log(2018 - this.birthYear); } } person.calcAge(); // "this" refers to "person", because "calcAge" was called with //"person" object reference let calculateAge = person.calcAge; calculateAge(); // "this" refers to the global window object, because no object reference was given詞法環(huán)境
官方es6文檔對(duì)詞法環(huán)境有如下解釋?zhuān)?/p>
詞匯環(huán)境是一種規(guī)范類(lèi)型,用于根據(jù)ECMAScript代碼的詞法嵌套結(jié)構(gòu)定義標(biāo)識(shí)符與特定變量和函數(shù)的關(guān)聯(lián)。詞匯環(huán)境由environment record(譯者:實(shí)在不知道咋翻)和外部詞匯環(huán)境的可能為null的引用組成。
(譯者:硬翻的,有點(diǎn)怪)
簡(jiǎn)而言之,詞匯環(huán)境是一種包含標(biāo)識(shí)符變量映射的結(jié)構(gòu)(此處標(biāo)識(shí)符指的是變量/函數(shù)的名稱(chēng),變量是對(duì)實(shí)際對(duì)象【包括函數(shù)類(lèi)型的對(duì)象】或原始值的引用)。
現(xiàn)在,詞法環(huán)境由兩部分組成:
(1)environment record
(2)外部環(huán)境的引用
1、environment record是存放變量和函數(shù)聲明的一個(gè)地方
2、對(duì)外部環(huán)境的引用意味著它可以訪(fǎng)問(wèn)其外部詞匯環(huán)境。
有兩種類(lèi)型的詞法環(huán)境:
一種是全局環(huán)境(在全局執(zhí)行上下文里),它沒(méi)有外部環(huán)境的引用。它的外部環(huán)境引用為null。它有全局對(duì)象(window對(duì)象)和關(guān)聯(lián)的方法和屬性(例如數(shù)組方法),以及任何用戶(hù)定義的全局變量,并且this的值指向全局對(duì)象。
一種是函數(shù)環(huán)境,它存放用戶(hù)在函數(shù)里定義的變量。并且外部環(huán)境可以指向全局環(huán)境,或者是外層函數(shù)環(huán)境。
筆記——對(duì)于函數(shù)環(huán)境,environment record還包含一個(gè)arguments對(duì)象,該對(duì)象包含索引和傳遞給函數(shù)的參數(shù)之間的映射以及傳遞給函數(shù)的參數(shù)的長(zhǎng)度(數(shù)量)。例如,下面函數(shù)的參數(shù)對(duì)象如下所示:
function foo(a, b) { var c = a + b; } foo(2, 3); // argument object Arguments: {0: 2, 1: 3, length: 2},
environment record也有兩種類(lèi)型:
聲明性environment record存儲(chǔ)變量,函數(shù)和參數(shù)。 一個(gè)函數(shù)環(huán)境包含聲明性environment record。
對(duì)象environment record用于定義在全局執(zhí)行上下文中出現(xiàn)的變量和函數(shù)的關(guān)聯(lián)。全局環(huán)境包含對(duì)象environment record。
抽象地說(shuō),詞法環(huán)境在偽代碼中看起來(lái)像這樣:
GlobalExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // Identifier bindings go here } outer:變量環(huán)境} } FunctionExectionContext = { LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // Identifier bindings go here } outer: } }
它也是一個(gè)詞法環(huán)境,其EnvironmentRecord包含由此執(zhí)行上下文中的VariableStatements創(chuàng)建的綁定。
如上所述,變量環(huán)境也是一個(gè)詞匯環(huán)境,因此它具有上面定義的詞法環(huán)境的所有屬性。
在es6,詞法環(huán)境和變量環(huán)境的不同在于前者用于存儲(chǔ)函數(shù)聲明和變量(let和const)綁定,而后者用于存儲(chǔ)變量(var)的綁定。
來(lái)看個(gè)例子:
let a = 20; const b = 30; var c; function multiply(e, f) { var g = 20; return e * f * g; } c = multiply(20, 30);
然后執(zhí)行上下文會(huì)像這樣:
GlobalExectionContext = { ThisBinding:, LexicalEnvironment: { EnvironmentRecord: { Type: "Object", // Identifier bindings go here a: < uninitialized >, b: < uninitialized >, multiply: < func > } outer: }, VariableEnvironment: { EnvironmentRecord: { Type: "Object", // Identifier bindings go here c: undefined, } outer: } } FunctionExectionContext = { ThisBinding: , LexicalEnvironment: { EnvironmentRecord: { Type: "Declarative", // Identifier bindings go here Arguments: {0: 20, 1: 30, length: 2}, }, outer: }, VariableEnvironment: { EnvironmentRecord: { Type: "Declarative", // Identifier bindings go here g: undefined }, outer: } }
筆記——函數(shù)multiply調(diào)用時(shí)才會(huì)創(chuàng)建函數(shù)執(zhí)行上下文。
正如你所注意到的一樣,let和const定義的變量沒(méi)有綁定任何值,但var定義的變量為undefined
這是因?yàn)樵趧?chuàng)建階段,掃描代碼尋找變量和函數(shù)聲明時(shí),函數(shù)聲明完全存儲(chǔ)在環(huán)境中,但變量最初設(shè)置為undefined(var)或保持為為初始化(let、const)。
(譯者:就是var會(huì)聲明提升,而let和const不會(huì))
這就是為什么你可以在變量聲明前訪(fǎng)問(wèn)到var定義的變量,而訪(fǎng)問(wèn)let和const定義的變量則會(huì)拋出引用錯(cuò)誤。
這就是js的變量提升。
執(zhí)行階段這是整篇文章中最簡(jiǎn)單的部分。 在此階段,完成對(duì)所有這些變量的分配,最后執(zhí)行代碼。
筆記——在執(zhí)行階段,如果js引擎在源代碼聲明的實(shí)際位置找不到let變量的值,那么它將為其分配undefined值。
結(jié)論現(xiàn)在,我們已經(jīng)了js的部分執(zhí)行原理,雖然理解了這些概念不一定能讓你成為出色的js開(kāi)發(fā)者,但是明白了上述的概念能讓你更好理解js的其它概念,例如變量提升、閉包。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/98959.html
摘要:每次調(diào)用函數(shù)時(shí),都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文。理解執(zhí)行上下文和堆??梢宰屇私獯a為什么要計(jì)算您最初沒(méi)有預(yù)料到的不同值的原因。 首發(fā):https://www.love85g.com/?p=1723 在這篇文章中,我將深入研究JavaScript最基本的部分之一,即執(zhí)行上下文。在這篇文章的最后,您應(yīng)該更清楚地了解解釋器要做什么,為什么在聲明一些函數(shù)/變量之前可以使用它們,以及它們的值是如何...
摘要:本章會(huì)對(duì)語(yǔ)言引擎,運(yùn)行時(shí),調(diào)用棧做一個(gè)概述。調(diào)用棧只是一個(gè)單線(xiàn)程的編程語(yǔ)言,這意味著它只有一個(gè)調(diào)用棧。查看如下代碼當(dāng)引擎開(kāi)始執(zhí)行這段代碼的時(shí)候,調(diào)用棧會(huì)被清空。之后,產(chǎn)生如下步驟調(diào)用棧中的每個(gè)入口被稱(chēng)為堆棧結(jié)構(gòu)。 原文請(qǐng)查閱這里,本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原...
摘要:本章會(huì)對(duì)語(yǔ)言引擎,運(yùn)行時(shí),調(diào)用棧做一個(gè)概述。調(diào)用棧只是一個(gè)單線(xiàn)程的編程語(yǔ)言,這意味著它只有一個(gè)調(diào)用棧。查看如下代碼當(dāng)引擎開(kāi)始執(zhí)行這段代碼的時(shí)候,調(diào)用棧會(huì)被清空。之后,產(chǎn)生如下步驟調(diào)用棧中的每個(gè)入口被稱(chēng)為堆棧結(jié)構(gòu)。 原文請(qǐng)查閱這里,本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原...
摘要:調(diào)用棧是一種數(shù)據(jù)結(jié)構(gòu),它記錄了我們?cè)诔绦蛑械奈恢?。?dāng)從這個(gè)函數(shù)返回的時(shí)候,就會(huì)將這個(gè)函數(shù)從棧頂彈出,這就是調(diào)用棧做的事情。而且這不是唯一的問(wèn)題,一旦你的瀏覽器開(kāi)始處理調(diào)用棧中的眾多任務(wù),它可能會(huì)停止響應(yīng)相當(dāng)長(zhǎng)一段時(shí)間。 原文地址: https://blog.sessionstack.com... PS: 好久沒(méi)寫(xiě)東西了,最近一直在準(zhǔn)備寫(xiě)一個(gè)自己的博客,最后一些技術(shù)方向已經(jīng)敲定了,又可以...
摘要:技術(shù)上來(lái)說(shuō)這個(gè)機(jī)制被稱(chēng)為動(dòng)態(tài)分配或代理。定義類(lèi)一個(gè)類(lèi)是一個(gè)正式的抽象集,它規(guī)定了對(duì)象的初始狀態(tài)和行為。技術(shù)上來(lái)說(shuō)一個(gè)類(lèi)表示構(gòu)造函數(shù)原型的組合。因此構(gòu)造函數(shù)創(chuàng)建對(duì)象并自動(dòng)設(shè)置新創(chuàng)建實(shí)例的原型。第二次調(diào)用時(shí),相同的上下文再次被壓入棧并恢復(fù)。 原文:JavaScript. The Core: 2nd Edition作者:Dmitry Soshnikov 文章其他語(yǔ)言版本:俄語(yǔ) 這篇文章是 ...
閱讀 2401·2023-04-26 00:28
閱讀 3130·2019-08-30 15:55
閱讀 2809·2019-08-30 12:47
閱讀 1623·2019-08-29 11:04
閱讀 3319·2019-08-28 18:14
閱讀 1026·2019-08-28 18:11
閱讀 1727·2019-08-26 18:36
閱讀 3446·2019-08-23 18:21