日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 前端技术 > javascript >内容正文

javascript

# JavaScript中的执行上下文和队列(栈)的关系?

發(fā)布時間:2024/4/13 javascript 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 # JavaScript中的执行上下文和队列(栈)的关系? 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
  • 原文:What is the Execution Context & Stack in JavaScript?
  • git地址:JavaScript中的執(zhí)行上下文和隊列(棧)的關(guān)系?
  • 導(dǎo)讀:以前總是看到相關(guān)文章提到什么變量提升,函數(shù)提升啥的,什么函數(shù)提升優(yōu)先級大于變量的,總是知其然,不知其所以然,當(dāng)面試官拿著同一name,卻不斷function, 和var賦值,然后讓你告訴他每一個階段該是什么值的時候,拿著啥變量提升和函數(shù)提升是解釋不通的,至少我不能-_-。David Shariff的這篇文章為我們講述了其中的原理,讓人看了豁然開朗
在這篇文章中,我將深入探討JavaScript的一個最基本的部分,執(zhí)行上下文。 在本文結(jié)束時,您會更清楚解釋器都做了些什么,以至于某些函數(shù)、變量在聲明它們之前就可以使用,它們的值是如何確定的。

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

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

  • global 代碼 -- 首次執(zhí)行代碼的默認(rèn)環(huán)境
  • function 代碼 -- 每當(dāng)執(zhí)行流程進入函數(shù)體時
  • Eval 代碼 -- 要在內(nèi)部eval 函數(shù)內(nèi)執(zhí)行的文本

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

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

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

執(zhí)行上下文堆棧

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

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

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

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

代碼只調(diào)用自身3次,將i的值遞增1.每次調(diào)用foo函數(shù)時,都會創(chuàng)建一個新的執(zhí)行上下文。一旦執(zhí)行完成,它就會彈出堆棧并且將控制權(quán)交給它下面的上下文,直到再次到達(dá)global context(koa2的洋蔥圖想到了沒?)

以下是執(zhí)行隊列的5個關(guān)鍵點:

  • 單線程、
  • 同步執(zhí)行
  • 全局上下文
  • 無限級的函數(shù)上下文
  • 每個函數(shù)調(diào)用都會創(chuàng)建一個新的執(zhí)行上下文(execution context),包括對自身的調(diào)用(遞歸)

執(zhí)行上下文詳情

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

  • 創(chuàng)建階段 [調(diào)用函數(shù)時,但在執(zhí)行任何代碼之前]:
    • 創(chuàng)建作用域鏈。
    • 創(chuàng)建變量(variables),函數(shù)(functions )和參數(shù)(arguments)
    • 確定"this"。
  • 激活/執(zhí)行階段:
    • var 賦值,(function聲明)指向函數(shù),解釋/執(zhí)行代碼

    可以將每個execution context概念上表示為具有3個屬性的對象:

    executionContextObj = {'scopeChain': { /* variableObject + all parent execution context's variableObject */ },'variableObject': { /* function arguments / parameters, inner variable and function declarations */ },'this': {} }

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

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

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

  • 遇到函數(shù)調(diào)用。
  • 在執(zhí)行function代碼之前,創(chuàng)建執(zhí)行上下文(execution context)。
  • 進入創(chuàng)建階段:

    • 初始化作用域鏈(Scope Chain)。
    • 創(chuàng)建變量對象(variable object):

      • 創(chuàng)建arguments object,檢查參數(shù)的上下文,初始化名稱和值并創(chuàng)建引用副本。
      • 掃描上下文以獲取函數(shù)聲明:

        • 對于找到的每個函數(shù),在variable object中創(chuàng)建一個以函數(shù)名稱為屬性的鍵值對,值指向內(nèi)存中函數(shù)的引用指針。
        • 如果函數(shù)名已存在,則將覆蓋引用指針值。
      • 掃描上下文以獲取變量聲明:

        • 對于找到的每個變量聲明,在variable object中創(chuàng)建一個以變量名為屬性的鍵值對,值初始化為undefined。
        • 如果變量名已經(jīng)存在于variable object,則不執(zhí)行任何操作并繼續(xù)掃描。
    • 確定"this"在上下文中的值。
  • 激活/執(zhí)行階段:

    • 在上下文中運行/解析函數(shù)體的代碼,并在代碼逐行執(zhí)行時為變量賦值。
  • 我們來看一個例子:

    function foo(i) {var a = 'hello';var b = function privateB() {};function c() {} }foo(22);

    在調(diào)用時foo(22),creation stage長這樣子:

    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í)行流程進入函數(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ì)解釋為什么會發(fā)生這種情況,而在你掌握了關(guān)于解釋器如何創(chuàng)建activation object后,會很容易理解。示例:

    (function() {console.log(typeof foo); // function pointerconsole.log(typeof bar); // undefinedvar foo = 'hello',bar = function() {return 'world';};function foo() {return 'hello';}}());?

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

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

      • 如果我們遵循creation stage,我們知道變量在activation / code execution stage之前就創(chuàng)建了。所以當(dāng)功能流程開始執(zhí)行時,foo早就在activation object中定義了。
    • foo是聲明了兩次,為什么顯示foo的是 function ,__不是__ undefined string?

      • 即使foo聲明了兩次,我們也知道在creation stage函數(shù)在變量之前就在activation objectbefore上創(chuàng)建了,如果屬性名已經(jīng)存在于activation object,解釋器會忽略掉此次聲明。
      • 因此,首先會在activation object上創(chuàng)建一個foo()的引用,當(dāng)解釋器到達(dá)時var foo,屬性名稱foo存在,所以代碼什么也不做,然后繼續(xù)。
    • 為什么 bar 是 undefined?

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

    概要

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

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

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

    拓展

    • ECMA-262-3 in detail. Chapter 2. Variable object
    • Identifier Resolution, Execution Contexts and scope chains

    總結(jié)

    以上是生活随笔為你收集整理的# JavaScript中的执行上下文和队列(栈)的关系?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 欧美日韩高清在线观看 | 香蕉视频黄色在线观看 | 操亚洲| 爱爱的网站 | 国产极品美女高潮无套嗷嗷叫酒店 | 国产精品99久久久久久动医院 | 深夜免费在线视频 | 成人三区 | 美女脱裤子让男人捅 | 精品国产乱码久久久久久1区二区 | 亚洲做受高潮无遮挡 | 国产高清色 | 4444亚洲人成无码网在线观看 | 日本在线二区 | a无一区二区三区 | 看国产黄色片 | 日韩av一区二区在线播放 | 欧美一级免费在线观看 | 国产盗摄精品 | 中文字幕有码在线观看 | 涩涩av| 就爱啪啪网 | 午夜亚洲天堂 | 黄色裸体网站 | jlzzjlzz国产精品久久 | 蜜臀aⅴ国产精品久久久国产老师 | 国产精品免费无遮挡无码永久视频 | 午夜黄色网址 | 午夜激情导航 | 国产l精品国产亚洲区久久 午夜青青草 | 久久婷婷五月综合 | 私密视频在线观看 | av一区三区| 美女网站视频在线观看 | 黄色一级大片免费版 | 国产一区二区三区三州 | 日韩黄色av | 在线免费观看你懂的 | 亚洲黄色大片 | 91干干干| 91黄视频在线观看 | 亚洲剧情在线 | zzji欧美大片 | 在线观看精品 | 污的视频在线观看 | 好吊色欧美一区二区三区视频 | 一级二级三级视频 | 日韩一区欧美一区 | 成人区人妻精品一区二区网站 | 成人高清网站 | 久久久久久98 | 中文字幕在线视频一区二区三区 | 三级自拍视频 | 国产精品无码久久久久一区二区 | 国产成人自拍在线 | 性插视频在线观看 | yy4138理论片动漫理论片 | 成人一区二区三区在线观看 | 日韩和欧美一区二区 | 97色网| 亚洲人成人一区二区在线观看 | 国产精品无码AV | 激情偷拍| 久久99伊人 | 高跟鞋av| sese亚洲 | 巨胸大乳www视频免费观看 | 啪啪啪一区二区 | 干一干操一操 | 又骚又黄的视频 | 福利资源在线观看 | 午夜剧场91| 人妻精品久久久久中文 | 久久久久久亚洲中文字幕无码 | 亚洲精品一区二区三区四区 | 偷拍一区二区三区四区 | 成人免费视频网站 | 久久久久亚洲av片无码 | 国产成人在线一区二区 | 热热色av | xxxxxx黄色 | 在线亚洲+欧美+日本专区 | 狠狠做深爱婷婷久久综合一区 | 亚洲国内在线 | 午夜免费视频网站 | 日本一区二区三区精品 | 91av在线免费| 久久久精选| 在办公室被c到呻吟的动态图 | 一区二区精品免费 | aaa午夜| 国产欧美精品一区二区色综合朱莉 | 久久精品国产熟女亚洲AV麻豆 | 国产偷人视频 | 久久精品久久久久 | 久久久二区 | 神马久久网 | 国产一区毛片 | 在线观看av黄色 |