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

資訊專欄INFORMATION COLUMN

JavaScript中的執(zhí)行上下文

cfanr / 2241人閱讀

摘要:如果在全局代碼中調(diào)用函數(shù),程序的順序流進(jìn)入被調(diào)用的函數(shù),創(chuàng)建新的執(zhí)行上下文并將其推送到執(zhí)行堆棧的頂部。每次調(diào)用函數(shù)時(shí),都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文。

翻譯:瘋狂的技術(shù)宅
鏈接:http://davidshariff.com/blog/...

本文首發(fā)微信公眾號(hào):jingchengyideng
歡迎關(guān)注,每天都給你推送新鮮的前端技術(shù)文章

在這篇文章中,我將深入探討JavaScript的最基本部分之一,即Execution Context(執(zhí)行上下文)。 在本文結(jié)束時(shí),你應(yīng)該對(duì)解釋器了解得更清楚:為什么在聲明它們之前可以使用某些函數(shù)或變量?以及它們的值是如何確定的?

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

JavaScript的執(zhí)行環(huán)境非常重要,當(dāng)JavaScript代碼在行時(shí),會(huì)被預(yù)處理為以下情況之一:

Global code - 首次執(zhí)行代碼的默認(rèn)環(huán)境。

Function code - 每當(dāng)執(zhí)行流程進(jìn)入函數(shù)體時(shí)。

Eval code - 要在eval函數(shù)內(nèi)執(zhí)行的文本。

你可以閱讀大量涉及作用域的在線資料,不過為了使事情更容易理解,讓我們將術(shù)語“執(zhí)行上下文”視為當(dāng)前代碼的運(yùn)行環(huán)境或作用域。接下來讓我們看一個(gè)包含global和function / local上下文的代碼示例。

這里沒有什么特別之處,我們有一個(gè)由紫色邊框表示的全局上下文,和由綠色,藍(lán)色和橙色邊框表示的3個(gè)不同的函數(shù)上下文。 只能有1個(gè)全局上下文,可以從程序中的任何其他上下文訪問。

你可以擁有任意數(shù)量的函數(shù)上下文,并且每個(gè)函數(shù)調(diào)用都會(huì)創(chuàng)建一個(gè)新的上下文,從而創(chuàng)建一個(gè)私有作用域,其中無法從當(dāng)前函數(shù)作用域外直接訪問函數(shù)內(nèi)部聲明的任何內(nèi)容。 在上面的示例中,函數(shù)可以訪問在其當(dāng)前上下文之外聲明的變量,但外部上下文無法訪問在其中聲明的變量或函數(shù)。 為什么會(huì)這樣呢? 這段代碼究竟是如何處理的?

Execution Context Stack(執(zhí)行上下文堆棧)

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

我們已經(jīng)知道,當(dāng)瀏覽器首次加載腳本時(shí),它默認(rèn)進(jìn)入全局上下文執(zhí)行。 如果在全局代碼中調(diào)用函數(shù),程序的順序流進(jìn)入被調(diào)用的函數(shù),創(chuàng)建新的執(zhí)行上下文并將其推送到執(zhí)行堆棧的頂部。

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

(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á)到全局上下文。

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

單線程。

同步執(zhí)行。

一個(gè)全局上下文。

任意多個(gè)函數(shù)上下文。

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

執(zhí)行上下文的細(xì)節(jié)

所以我們現(xiàn)在知道每次調(diào)用一個(gè)函數(shù)時(shí),都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文。 但是,在JavaScript解釋器中,對(duì)執(zhí)行上下文的每次調(diào)用都有兩個(gè)階段:

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

創(chuàng)建作用域鏈。

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

確定“this”的值。

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

分配值,引用函數(shù)和解釋/執(zhí)行代碼。

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

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

在調(diào)用該函數(shù),并且在實(shí)際執(zhí)行函數(shù)之前,會(huì)創(chuàng)建這個(gè)executionContextObj。 這被稱為第1階段,即創(chuàng)造階段。 這時(shí)解釋器通過掃描函數(shù)傳遞的實(shí)參或形參、本地函數(shù)聲明和局部變量聲明來創(chuàng)建executionContextObj。 此掃描的結(jié)果將成為executionContextObj中的variableObject。

以下是解釋器如何預(yù)處理代碼的偽代碼概述:

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

在執(zhí)行功能代碼之前,創(chuàng)建執(zhí)行上下文

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

初始化作用域鏈。

創(chuàng)建variable object

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

掃描上下文以獲取函數(shù)聲明:

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

如果函數(shù)名已存在,則將覆蓋引用指針值。

掃描上下文以獲取變量聲明:

對(duì)于找到的每個(gè)變量聲明,在variable object中創(chuàng)建一個(gè)屬性作為變量名稱,并將該值初始化為undefined

如果變量名稱已存在于variable object中,則不執(zhí)行任何操作并繼續(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í)參除外。創(chuàng)建階段完成后,執(zhí)行流程進(jìn)入函數(shù),激活/代碼執(zhí)行階段在函數(shù)執(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)于hoisting

你可以找到許多使用JavaScript定義術(shù)語hoisting的在線資源,解釋變量和函數(shù)聲明被hoisting到其函數(shù)范圍的頂部。 但是沒有人能夠詳細(xì)解釋為什么會(huì)發(fā)生這種情況,掌握了關(guān)于解釋器如何創(chuàng)建激活對(duì)象的新知識(shí),很容易理解為什么。 請(qǐng)看下面的代碼示例:

(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í),已經(jīng)在激活對(duì)象中定義了foo。

Foo被聲明兩次,為什么foo顯示為function而不是undefinedstring?

即使foo被聲明兩次,我們通過創(chuàng)建階段知道函數(shù)在變量之前就被創(chuàng)建在激活對(duì)象上了,而且如果激活對(duì)象上已經(jīng)存在了屬性名稱,我們只是繞過了聲明這一步驟。

因此,首先在激活對(duì)象上創(chuàng)建對(duì)函數(shù)foo()的引用,并且當(dāng)解釋器到達(dá)var foo時(shí),我們已經(jīng)看到屬性名稱foo存在,因此代碼不執(zhí)行任何操作并繼續(xù)處理。

為什么bar未定義?

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

總結(jié)

希望到這里你已經(jīng)能夠很好地掌握了JavaScript解釋器如何預(yù)處理你的代碼。 理解執(zhí)行上下文和堆??梢宰屇懔私獗澈蟮脑颍簽槭裁创a預(yù)處理后的值和你預(yù)期的不一樣。

你認(rèn)為學(xué)習(xí)解釋器的內(nèi)部工作原理是多此一舉還是非常必要的呢? 了解執(zhí)行上下文階段是否能夠幫你你寫出更好的JavaScript呢?

進(jìn)一步閱讀

ECMA-262 5th Edition (http://www.ecma-international...

ECMA-262-3 in detail. Chapter 2. Variable object (http://dmitrysoshnikov.com/ec...

Identifier Resolution, Execution Contexts and scope chains (http://jibbering.com/faq/note...

本文首發(fā)微信公眾號(hào):jingchengyideng


歡迎掃描二維碼關(guān)注公眾號(hào),每天都給你推送新鮮的前端技術(shù)文章

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

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

相關(guān)文章

  • JavaScript深入之執(zhí)行下文

    摘要:深入系列第三篇,講解執(zhí)行上下文棧的是如何執(zhí)行的,也回答了第二篇中的略難的思考題。 JavaScript深入系列第三篇,講解執(zhí)行上下文棧的是如何執(zhí)行的,也回答了第二篇中的略難的思考題。 順序執(zhí)行? 如果要問到 JavaScript 代碼執(zhí)行順序的話,想必寫過 JavaScript 的開發(fā)者都會(huì)有個(gè)直觀的印象,那就是順序執(zhí)行,畢竟: var foo = function () { ...

    codecraft 評(píng)論0 收藏0
  • JavaScript執(zhí)行下文執(zhí)行環(huán)境

    摘要:也就是說,當(dāng)代碼執(zhí)行的時(shí)候,會(huì)進(jìn)入不同的執(zhí)行上下文,這些執(zhí)行上下文就構(gòu)成了一個(gè)執(zhí)行上下文棧,。它是一個(gè)與上下文相關(guān)的特殊對(duì)象,其中存儲(chǔ)了在上下文中定義的變量和函數(shù)聲明。 明白的人,看標(biāo)題這么寫,會(huì)發(fā)現(xiàn)是有問題的,對(duì)的,在JavaScript中執(zhí)行上下文與執(zhí)行環(huán)境是同一個(gè)東西,標(biāo)題這么寫肯定是有問題的。但是有些人是搞不清執(zhí)行上下文與執(zhí)行環(huán)境的,所以我才這么寫,以便于他們好搜索到。下面我們...

    ZHAO_ 評(píng)論0 收藏0
  • 深入理解JavaScript執(zhí)行下文執(zhí)行

    摘要:執(zhí)行上下文和執(zhí)行棧是中關(guān)鍵概念之一,是難點(diǎn)之一。理解執(zhí)行上下文和執(zhí)行棧同樣有助于理解其他的概念如提升機(jī)制作用域和閉包等。函數(shù)執(zhí)行完成,函數(shù)的執(zhí)行上下文出棧,并且被銷毀。 前言 如果你是一名 JavaScript 開發(fā)者,或者想要成為一名 JavaScript 開發(fā)者,那么你必須知道 JavaScript 程序內(nèi)部的執(zhí)行機(jī)制。執(zhí)行上下文和執(zhí)行棧是JavaScript中關(guān)鍵概念之一,是Ja...

    silenceboy 評(píng)論0 收藏0
  • 深入理解JavaScript執(zhí)行下文執(zhí)行

    摘要:執(zhí)行上下文和執(zhí)行棧是中關(guān)鍵概念之一,是難點(diǎn)之一。理解執(zhí)行上下文和執(zhí)行棧同樣有助于理解其他的概念如提升機(jī)制作用域和閉包等。函數(shù)執(zhí)行完成,函數(shù)的執(zhí)行上下文出棧,并且被銷毀。 前言 如果你是一名 JavaScript 開發(fā)者,或者想要成為一名 JavaScript 開發(fā)者,那么你必須知道 JavaScript 程序內(nèi)部的執(zhí)行機(jī)制。執(zhí)行上下文和執(zhí)行棧是JavaScript中關(guān)鍵概念之一,是Ja...

    leiyi 評(píng)論0 收藏0
  • 深入學(xué)習(xí)js之——執(zhí)行下文

    摘要:當(dāng)遇到函數(shù)調(diào)用時(shí),引擎為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文并把它壓入當(dāng)前執(zhí)行棧的頂部。參考鏈接理解中的執(zhí)行上下文和執(zhí)行棧深入之執(zhí)行上下文棧 開篇 作為一個(gè)JavaScript的程序開發(fā)者,如果被問到JavaScript代碼的執(zhí)行順序,你腦海中是不是有一個(gè)直觀的印象 -- JavaScript 是順序執(zhí)行的,可事實(shí)真的是這樣的嗎? 讓我們首先看兩個(gè)小例子: var foo = functio...

    Lucky_Boy 評(píng)論0 收藏0
  • 【進(jìn)階1-1期】理解JavaScript 中的執(zhí)行下文執(zhí)行

    摘要:首次運(yùn)行代碼時(shí),會(huì)創(chuàng)建一個(gè)全局執(zhí)行上下文并到當(dāng)前的執(zhí)行棧中。執(zhí)行上下文的創(chuàng)建執(zhí)行上下文分兩個(gè)階段創(chuàng)建創(chuàng)建階段執(zhí)行階段創(chuàng)建階段確定的值,也被稱為。 (關(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期,每期重點(diǎn)攻克一個(gè)面試重難點(diǎn),如果你還不了解本進(jìn)...

    import. 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

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