成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

【譯】JavaScript中的執(zhí)行上下文和堆棧是什么?

miguel.jiang / 2236人閱讀

摘要:每次調(diào)用函數(shù)時(shí),都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文。理解執(zhí)行上下文和堆??梢宰屇私獯a為什么要計(jì)算您最初沒有預(yù)料到的不同值的原因。

首發(fā):https://www.love85g.com/?p=1723

在這篇文章中,我將深入研究JavaScript最基本的部分之一,即執(zhí)行上下文。在這篇文章的最后,您應(yīng)該更清楚地了解解釋器要做什么,為什么在聲明一些函數(shù)/變量之前可以使用它們,以及它們的值是如何確定的。

什么是執(zhí)行上下文?

當(dāng)代碼在JavaScript中運(yùn)行時(shí),執(zhí)行它的環(huán)境是非常重要的,并被評估為以下之一:

1:全局代碼——第一次執(zhí)行代碼的默認(rèn)環(huán)境。

2:函數(shù)代碼——每當(dāng)執(zhí)行流進(jìn)入函數(shù)體時(shí)。

3:要在內(nèi)部Eval函數(shù)中執(zhí)行的文本。

您可以在線閱讀大量參考資料,其中涉及scope ,本文的目的是使事情更容易理解,讓我們將術(shù)語 execution context(執(zhí)行上下文) 看作當(dāng)前代碼正在計(jì)算的環(huán)境/范圍?,F(xiàn)在,討論得夠多了,讓我們來看一個(gè)包含 global 和 function / local 上下文計(jì)算代碼的示例。

這里沒什么特別的,我們有1個(gè) global context 用紫色邊框表示,3個(gè)不同的function contexts 用綠色、藍(lán)色和橙色邊框表示。只能有一個(gè) global context ,它可以從程序中的任何其他上下文訪問。

您可以有任意數(shù)量的 function contexts ,并且每個(gè)函數(shù)調(diào)用都創(chuàng)建一個(gè)新的上下文,該上下文創(chuàng)建一個(gè)私有范圍,其中函數(shù)內(nèi)部聲明的任何內(nèi)容都不能從當(dāng)前函數(shù)范圍外部直接訪問。在上面的例子中,一個(gè)函數(shù)可以訪問當(dāng)前上下文之外聲明的變量,但是外部上下文不能訪問其中聲明的變量/函數(shù)。為什么會(huì)這樣?這段代碼究竟是如何計(jì)算的?

執(zhí)行上下文堆棧

瀏覽器中的JavaScript解釋器是作為一個(gè)線程實(shí)現(xiàn)的。這實(shí)際上意味著,在瀏覽器中,一次只能發(fā)生一件事,其他操作或事件將排隊(duì)在所謂的執(zhí)行堆棧中。下圖是單線程棧的抽象視圖:

我們已經(jīng)知道,當(dāng)瀏覽器第一次加載腳本時(shí),默認(rèn)情況下它會(huì)進(jìn)入 global execution context 。如果在全局代碼中調(diào)用一個(gè)函數(shù),程序的序列流將進(jìn)入被調(diào)用的函數(shù),創(chuàng)建一個(gè)新的 execution context 并將該上下文推到 execution stack 的頂部。

如果在當(dāng)前函數(shù)中調(diào)用另一個(gè)函數(shù),也會(huì)發(fā)生同樣的事情。代碼的執(zhí)行流進(jìn)入內(nèi)部函數(shù),該函數(shù)創(chuàng)建一個(gè)新的 execution context ,并將其推到現(xiàn)有堆棧的頂部。瀏覽器將始終執(zhí)行位于堆棧頂部的當(dāng)前 execution context ,一旦函數(shù)執(zhí)行完當(dāng)前
execution context ,它將從堆棧頂部彈出,將控制權(quán)返回到當(dāng)前堆棧中下面的上下文。下面的例子展示了一個(gè)遞歸函數(shù)和程序的 execution stack :

(function foo(i) {
    if (i === 3) {
        return;
    }
    else {
        foo(++i);
    }
}(0));

代碼簡單地調(diào)用自身3次,將i的值增加1。每次調(diào)用函數(shù)foo時(shí),都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文。一旦上下文執(zhí)行完畢,它就會(huì)從堆棧中彈出并返回到它下面的上下文,直到再次到達(dá) global context 為止。

關(guān)于執(zhí)行堆棧,有5個(gè)關(guān)鍵點(diǎn)需要記住:

1:單線程的。

2:同步執(zhí)行。

3:1個(gè)全局上下文。

4:無限的函數(shù)上下文。

5:每個(gè)函數(shù)調(diào)用都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文,甚至是對自身的調(diào)用。

詳細(xì)執(zhí)行上下文

現(xiàn)在我們知道,每次調(diào)用一個(gè)函數(shù),都會(huì)創(chuàng)建一個(gè)新的 execution context 。然而,在JavaScript解釋器中,對 execution context 的每個(gè)調(diào)用都有兩個(gè)階段:

1:創(chuàng)建階段[當(dāng)函數(shù)被調(diào)用,但在執(zhí)行任何代碼之前]:

創(chuàng)建范圍鏈。

創(chuàng)建變量、函數(shù)和參數(shù)。

確定“this”的值。

2:激活/代碼執(zhí)行階段:

為函數(shù)賦值、引用并解釋/執(zhí)行代碼。

可以將每個(gè) execution context (執(zhí)行上下文)概念上表示為一個(gè)具有3個(gè)屬性的對象:

executionContextObj = {
    "scopeChain": { /* 變量對象+所有父執(zhí)行上下文的變量對象 */ },
    "variableObject": { /* 函數(shù)參數(shù)/參數(shù),內(nèi)部變量和函數(shù)聲明 */ },
    "this": {}
}

激活/變量對象[AO/VO]

這個(gè) executionContextObj 在調(diào)用函數(shù)時(shí)創(chuàng)建,但在實(shí)際函數(shù)執(zhí)行之前創(chuàng)建。這被稱為階段1,創(chuàng)建階段。在這里,解釋器通過掃描函數(shù)尋找傳入的參數(shù)或參數(shù)、局部函數(shù)聲明和局部變量聲明來創(chuàng)建executionContextObj 。該掃描的結(jié)果成為executionContextObj 中的variableObject。

下面是解釋器如何評估代碼的偽概述:

找到一些代碼來調(diào)用函數(shù)。

在執(zhí)行函數(shù)代碼之前,創(chuàng)建執(zhí)行上下文。

進(jìn)入創(chuàng)作階段:

初始化范圍鏈。

創(chuàng)建變量對象:

創(chuàng)建arguments對象,檢查參數(shù)上下文,初始化名稱和值,并創(chuàng)建引用副本。

掃描上下文中的函數(shù)聲明:

對于找到的每個(gè)函數(shù),在變量對象中創(chuàng)建一個(gè)屬性,該屬性是確切的函數(shù)名,該函數(shù)在內(nèi)存中有一個(gè)指向該函數(shù)的引用指針。

如果函數(shù)名已經(jīng)存在,則重寫引用指針值。

掃描上下文變量聲明:

對于找到的每個(gè)變量聲明,在變量對象中創(chuàng)建一個(gè)屬性,即變量名,并初始化值為undefined。

如果變量名已經(jīng)存在于變量對象中,則什么也不做,繼續(xù)掃描。

確定上下文中“this”的值。

激活/代碼執(zhí)行階段:

在上下文中運(yùn)行/解釋函數(shù)代碼,并在逐行執(zhí)行代碼時(shí)分配變量值。

讓我們來看一個(gè)例子:

function foo(i) {
    var a = "hello";
    var b = function privateB() {

    };
    function c() {

    }
}

foo(22);

調(diào)用foo(22)時(shí),創(chuàng)建階段如下:

fooExecutionContext = {
    scopeChain: { ... },
    variableObject: {
        arguments: {
            0: 22,
            length: 1
        },
        i: 22,
        c: pointer to function c()
        a: undefined,
        b: undefined
    },
    this: { ... }
}

如您所見,創(chuàng)建階段處理定義屬性的名稱,而不是為它們賦值,只有形式參數(shù)/參數(shù)例外。創(chuàng)建階段完成后,執(zhí)行流程進(jìn)入函數(shù),函數(shù)完成執(zhí)行后,激活/代碼執(zhí)行階段如下:

fooExecutionContext = {
    scopeChain: { ... },
    variableObject: {
        arguments: {
            0: 22,
            length: 1
        },
        i: 22,
        c: pointer to function c()
        a: "hello",
        b: pointer to function privateB()
    },
    this: { ... }
}

關(guān)于吊裝的說明

您可以在網(wǎng)上找到許多用JavaScript定義術(shù)語提升的資源,解釋變量和函數(shù)聲明被提升到函數(shù)作用域的頂部。但是,沒有人詳細(xì)解釋為什么會(huì)發(fā)生這種情況,而且有了解釋器如何創(chuàng)建 activation object(激活對象)的新知識,就很容易理解為什么會(huì)發(fā)生這種情況。以下面的代碼為例:

?(function() {

    console.log(typeof foo); // function pointer
    console.log(typeof bar); // undefined

    var foo = "hello",
        bar = function() {
            return "world";
        };

    function foo() {
        return "hello";
    }

}());?

我們現(xiàn)在可以回答的問題是:

為什么我們可以在聲明foo之前訪問它?

如果我們遵循創(chuàng)建階段,我們就知道在激活/代碼執(zhí)行階段之前已經(jīng)創(chuàng)建了變量。因此,當(dāng)函數(shù)流開始執(zhí)行時(shí),foo已經(jīng)在激活對象中定義。

Foo聲明了兩次,為什么Foo是函數(shù)而不是未定義或字符串?

盡管foo聲明了兩次,但從創(chuàng)建階段我們就知道函數(shù)是在變量之前在激活對象上創(chuàng)建的,如果激活對象上的屬性名已經(jīng)存在,那么我們只需繞過解密。

因此,首先在激活對象上創(chuàng)建對函數(shù)foo()的引用,當(dāng)解釋器到達(dá)var foo時(shí),我們已經(jīng)看到了屬性名foo的存在,所以代碼什么也不做,繼續(xù)執(zhí)行。

為什么bar沒有定義?

bar實(shí)際上是一個(gè)具有函數(shù)賦值的變量,我們知道這些變量是在創(chuàng)建階段創(chuàng)建的,但是它們是用undefined值初始化的。

總結(jié)

希望現(xiàn)在您已經(jīng)很好地理解了JavaScript解釋器是如何評估代碼的。理解執(zhí)行上下文和堆??梢宰屇私獯a為什么要計(jì)算您最初沒有預(yù)料到的不同值的原因。

您是否認(rèn)為了解解釋器的內(nèi)部工作方式對您的JavaScript知識來說是太大的開銷還是必需的?了解執(zhí)行上下文階段是否有助于編寫更好的JavaScript ?

原文:http://davidshariff.com/blog/...

歡迎關(guān)注小程序,感謝您的支持!

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/103817.html

相關(guān)文章

  • []了解Javascript中的執(zhí)行下文執(zhí)行堆棧

    摘要:理解執(zhí)行上下文和執(zhí)行堆棧對于理解的其它概念如提升,范圍和閉包至關(guān)重要。正確地理解執(zhí)行上下文和執(zhí)行堆棧將幫助你更好地使用開發(fā)應(yīng)用。引擎執(zhí)行位于執(zhí)行堆棧頂部的方法。當(dāng)調(diào)用時(shí),為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文,并且把它推入到當(dāng)前執(zhí)行堆棧。 By Sukhjinder Arora | Aug 28, 2018 原文 如果你是或者你想要成為一名js開發(fā)者,那么你必須了解js程序內(nèi)部的運(yùn)作。理解執(zhí)行...

    qujian 評論0 收藏0
  • JavaScript 工作原理之一-引擎,運(yùn)行時(shí),調(diào)用堆棧()

    摘要:本章會(huì)對語言引擎,運(yùn)行時(shí),調(diào)用棧做一個(gè)概述。調(diào)用棧只是一個(gè)單線程的編程語言,這意味著它只有一個(gè)調(diào)用棧。查看如下代碼當(dāng)引擎開始執(zhí)行這段代碼的時(shí)候,調(diào)用棧會(huì)被清空。之后,產(chǎn)生如下步驟調(diào)用棧中的每個(gè)入口被稱為堆棧結(jié)構(gòu)。 原文請查閱這里,本文采用知識共享署名 4.0 國際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原...

    Betta 評論0 收藏0
  • JavaScript 工作原理之一-引擎,運(yùn)行時(shí),調(diào)用堆棧()

    摘要:本章會(huì)對語言引擎,運(yùn)行時(shí),調(diào)用棧做一個(gè)概述。調(diào)用棧只是一個(gè)單線程的編程語言,這意味著它只有一個(gè)調(diào)用棧。查看如下代碼當(dāng)引擎開始執(zhí)行這段代碼的時(shí)候,調(diào)用棧會(huì)被清空。之后,產(chǎn)生如下步驟調(diào)用棧中的每個(gè)入口被稱為堆棧結(jié)構(gòu)。 原文請查閱這里,本文采用知識共享署名 4.0 國際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原...

    Alex 評論0 收藏0
  • JavaScript 如何工作:對引擎、運(yùn)行時(shí)、調(diào)用堆棧的概述

    摘要:調(diào)用棧是一種數(shù)據(jù)結(jié)構(gòu),它記錄了我們在程序中的位置。當(dāng)從這個(gè)函數(shù)返回的時(shí)候,就會(huì)將這個(gè)函數(shù)從棧頂彈出,這就是調(diào)用棧做的事情。而且這不是唯一的問題,一旦你的瀏覽器開始處理調(diào)用棧中的眾多任務(wù),它可能會(huì)停止響應(yīng)相當(dāng)長一段時(shí)間。 原文地址: https://blog.sessionstack.com... PS: 好久沒寫東西了,最近一直在準(zhǔn)備寫一個(gè)自己的博客,最后一些技術(shù)方向已經(jīng)敲定了,又可以...

    Warren 評論0 收藏0
  • []JavaScript的調(diào)用棧、回調(diào)隊(duì)列事件循環(huán)

    摘要:在這個(gè)視頻中,將的調(diào)用棧回調(diào)隊(duì)列和事件循環(huán)的內(nèi)容講的很清晰。調(diào)用??梢酝锩娣艝|西,可以在事件結(jié)束的時(shí)候把回調(diào)函數(shù)放進(jìn)回調(diào)隊(duì)列,然后是事件循環(huán)。為的時(shí)候這個(gè)過程看起來可能不明顯,除非考慮到調(diào)用棧的執(zhí)行環(huán)境和事件循環(huán)的情況。 譯者按這篇文章可以看做是對Philip Roberts 2014年在JSConf演講的《What the heck is the event loop anyway...

    YancyYe 評論0 收藏0

發(fā)表評論

0條評論

最新活動(dòng)
閱讀需要支付1元查看
<