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

資訊專欄INFORMATION COLUMN

# JavaScript中的執(zhí)行上下文和隊(duì)列(棧)的關(guān)系?

DangoSky / 1735人閱讀

摘要:為什么會(huì)這樣這段代碼究竟是如何運(yùn)行的執(zhí)行上下文堆棧瀏覽器中的解釋器單線程運(yùn)行。瀏覽器始終執(zhí)行位于堆棧頂部的,并且一旦函數(shù)完成執(zhí)行當(dāng)前操作,它將從堆棧頂部彈出,將控制權(quán)返回到當(dāng)前堆棧中的下方上下文。確定在上下文中的值。

原文:What is the Execution Context & Stack in JavaScript?

git地址:JavaScript中的執(zhí)行上下文和隊(duì)列(棧)的關(guān)系?

導(dǎo)讀:以前總是看到相關(guān)文章提到什么變量提升,函數(shù)提升啥的,什么函數(shù)提升優(yōu)先級(jí)大于變量的,總是知其然,不知其所以然,當(dāng)面試官拿著同一name,卻不斷function, 和var賦值,然后讓你告訴他每一個(gè)階段該是什么值的時(shí)候,拿著啥變量提升和函數(shù)提升是解釋不通的,至少我不能-_-。David Shariff的這篇文章為我們講述了其中的原理,讓人看了豁然開朗

在這篇文章中,我將深入探討JavaScript的一個(gè)最基本的部分,執(zhí)行上下文。 在本文結(jié)束時(shí),您會(huì)更清楚解釋器都做了些什么,以至于某些函數(shù)、變量在聲明它們之前就可以使用,它們的值是如何確定的。
什么是執(zhí)行上下文?

當(dāng)代碼在JavaScript中運(yùn)行時(shí),它的執(zhí)行環(huán)境非常重要,并且它們分為以下幾類:

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

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

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

為了便于理解,本文中執(zhí)行上下文是指:當(dāng)前被執(zhí)行的代碼的環(huán)境、作用域;接下來讓我們看一個(gè)執(zhí)行上下文中包含global、function content的代碼:

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

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

執(zhí)行上下文堆棧

瀏覽器中的JavaScript解釋器單線程運(yùn)行。這就意味著同一時(shí)間瀏覽器只執(zhí)行一件事,其它的事件在執(zhí)行隊(duì)列中排隊(duì)。下圖是單線程隊(duì)列的抽象視圖:

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

如果在當(dāng)前函數(shù)中調(diào)用另一個(gè)函數(shù),則會(huì)發(fā)生同樣的事情。代碼的執(zhí)行流程進(jìn)入內(nèi)部函數(shù),該函數(shù)創(chuàng)建一個(gè)execution context并推送到執(zhí)行隊(duì)列的頂部。瀏覽器始終執(zhí)行位于堆棧頂部的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)用foo函數(shù)時(shí),都會(huì)創(chuàng)建一個(gè)新的執(zhí)行上下文。一旦執(zhí)行完成,它就會(huì)彈出堆棧并且將控制權(quán)交給它下面的上下文,直到再次到達(dá)global context(koa2的洋蔥圖想到了沒?)

以下是執(zhí)行隊(duì)列的5個(gè)關(guān)鍵點(diǎn):

單線程、

同步執(zhí)行

全局上下文

無(wú)限級(jí)的函數(shù)上下文

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

執(zhí)行上下文詳情

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

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

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

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

確定"this"。

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

var 賦值,(function聲明)指向函數(shù),解釋/執(zhí)行代碼

可以將每個(gè)execution context概念上表示為具有3個(gè)屬性的對(duì)象:

executionContextObj = {
    "scopeChain": { /* variableObject + all parent execution context"s variableObject */ },
    "variableObject": { /* function arguments / parameters, inner variable and function declarations */ },
    "this": {}
}
激活/變量對(duì)象[AO / VO]

這executionContextObj是在調(diào)用函數(shù)時(shí),但在執(zhí)行實(shí)際函數(shù)之前創(chuàng)建的。這是第一階段:創(chuàng)建階段。這里,解釋器通過掃描傳入的參數(shù)或arguments、本地函數(shù)聲明和局部變量聲明來創(chuàng)建executionContextObj。這次掃描的結(jié)果就變成了executionContextObj.variableObject。

以下是解釋器如何解析代碼的偽概述:

遇到函數(shù)調(diào)用。

在執(zhí)行function代碼之前,創(chuàng)建執(zhí)行上下文(execution context)。

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

初始化作用域鏈(Scope Chain)。

創(chuàng)建變量對(duì)象(variable object):

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

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

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

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

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

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

如果變量名已經(jīng)存在于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)用時(shí)foo(22),creation stage長(zhǎng)這樣子:

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

正如您所看到的,creation stage定義屬性的name,不為它們賦值,但formal arguments / parameters(函數(shù)傳參,arguments)除外。一旦creation stage完成后,執(zhí)行流程進(jìn)入函數(shù)體,在函數(shù)已經(jīng)完成執(zhí)行之后的execution stage如下:

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

在很多JavaScript的資料中都提到了提升,解釋變量和函數(shù)聲明被提升到其作用域的頂部。但是,沒有人詳細(xì)解釋為什么會(huì)發(fā)生這種情況,而在你掌握了關(guān)于解釋器如何創(chuàng)建activation object后,會(huì)很容易理解。示例:

(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?

如果我們遵循creation stage,我們知道變量在activation / code execution stage之前就創(chuàng)建了。所以當(dāng)功能流程開始執(zhí)行時(shí),foo早就在activation object中定義了。

foo是聲明了兩次,為什么顯示foo的是 function ,__不是__ undefined string?

即使foo聲明了兩次,我們也知道在creation stage函數(shù)在變量之前就在activation objectbefore上創(chuàng)建了,如果屬性名已經(jīng)存在于activation object,解釋器會(huì)忽略掉此次聲明。

因此,首先會(huì)在activation object上創(chuàng)建一個(gè)foo()的引用,當(dāng)解釋器到達(dá)時(shí)var foo,屬性名稱foo存在,所以代碼什么也不做,然后繼續(xù)。

為什么 bar 是 undefined?

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

概要

希望到現(xiàn)在您已經(jīng)很好地掌握了JavaScript解釋器如何執(zhí)行您的代碼。理解執(zhí)行上下文和隊(duì)列可以讓您了解代碼沒有達(dá)到預(yù)期的原因

您是否認(rèn)為了解解釋器的內(nèi)部工作原理是您的JavaScript知識(shí)的重要組成部分?知道執(zhí)行上下文的每個(gè)階段是否有助于您編寫更好的JavaScript?

__注意__:有些人一直在問關(guān)于閉包,回調(diào),超時(shí)等,我將在在下一篇文章中涉及,主要概述作用域鏈與execution context的關(guān)系。

拓展

ECMA-262-3 in detail. Chapter 2. Variable object

Identifier Resolution, Execution Contexts and scope chains

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

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

相關(guān)文章

  • JavaScript調(diào)用

    摘要:調(diào)用棧是一種棧結(jié)構(gòu)它用來存儲(chǔ)計(jì)算機(jī)程序執(zhí)行時(shí)候其活躍子程序的信息。調(diào)用棧是解析器的一種機(jī)制。那是如何處理處理函數(shù)的調(diào)用關(guān)系的答案是調(diào)用棧。主線程之外存在一個(gè)任務(wù)隊(duì)列異步任務(wù)有了運(yùn)行結(jié)果會(huì)在任務(wù)隊(duì)列之中放置一個(gè)任務(wù)。 1:基本概念 棧(stack):用來保存簡(jiǎn)單的數(shù)據(jù)字段。 堆(heap):用來保存棧中簡(jiǎn)單的數(shù)據(jù)字段對(duì)指針的引用。 隊(duì)列:是一種先進(jìn)先出的線性數(shù)據(jù)結(jié)構(gòu)。 函數(shù)的調(diào)用的進(jìn)棧和...

    lushan 評(píng)論0 收藏0
  • 前端進(jìn)擊巨人(二):、堆、隊(duì)列、內(nèi)存空間

    摘要:中有三種數(shù)據(jù)結(jié)構(gòu)棧堆隊(duì)列。前端進(jìn)擊的巨人一執(zhí)行上下文與執(zhí)行棧,變量對(duì)象中解釋執(zhí)行棧時(shí),舉了一個(gè)乒乓球盒子的例子,來演示棧的存取方式,這里再舉個(gè)栗子搭積木。對(duì)于基本類型,棧中存儲(chǔ)的就是它自身的值,所以新內(nèi)存空間存儲(chǔ)的也是一個(gè)值。 面試經(jīng)常遇到的深淺拷貝,事件輪詢,函數(shù)調(diào)用棧,閉包等容易出錯(cuò)的題目,究其原因,都是跟JavaScript基礎(chǔ)知識(shí)不牢固有關(guān),下層地基沒打好,上層就是豆腐渣工程,...

    edgardeng 評(píng)論0 收藏0
  • 春招季如何橫掃 Javascript 面試核心考點(diǎn)(基礎(chǔ)版)?

    摘要:當(dāng)前函數(shù)執(zhí)行完成后,當(dāng)前函數(shù)的執(zhí)行上下文出棧,并等待垃圾回收。作用域與作用域鏈到來有全局作用域函數(shù)作用域和塊級(jí)作用域新增。 引言 Javascript是前端面試的重點(diǎn),本文重點(diǎn)梳理下 Javascript 中的常考知識(shí)點(diǎn),然后就一些容易出現(xiàn)的題目進(jìn)行解析。限于文章的篇幅,無(wú)法將知識(shí)點(diǎn)講解的面面俱到,本文只羅列了一些重難點(diǎn),如果想要了解更多內(nèi)容歡迎點(diǎn)擊我的博客。 一、變量類型 1.JS ...

    impig33 評(píng)論0 收藏0
  • 春招季如何橫掃 Javascript 面試核心考點(diǎn)(基礎(chǔ)版)?

    摘要:當(dāng)前函數(shù)執(zhí)行完成后,當(dāng)前函數(shù)的執(zhí)行上下文出棧,并等待垃圾回收。作用域與作用域鏈到來有全局作用域函數(shù)作用域和塊級(jí)作用域新增。 引言 Javascript是前端面試的重點(diǎn),本文重點(diǎn)梳理下 Javascript 中的??贾R(shí)點(diǎn),然后就一些容易出現(xiàn)的題目進(jìn)行解析。限于文章的篇幅,無(wú)法將知識(shí)點(diǎn)講解的面面俱到,本文只羅列了一些重難點(diǎn),如果想要了解更多內(nèi)容歡迎點(diǎn)擊我的博客。 一、變量類型 1.JS ...

    jayce 評(píng)論0 收藏0

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

0條評(píng)論

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