<span id="mktg5"></span>

<i id="mktg5"><meter id="mktg5"></meter></i>

        <label id="mktg5"><meter id="mktg5"></meter></label>
        最新文章專題視頻專題問(wèn)答1問(wèn)答10問(wèn)答100問(wèn)答1000問(wèn)答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
        問(wèn)答文章1 問(wèn)答文章501 問(wèn)答文章1001 問(wèn)答文章1501 問(wèn)答文章2001 問(wèn)答文章2501 問(wèn)答文章3001 問(wèn)答文章3501 問(wèn)答文章4001 問(wèn)答文章4501 問(wèn)答文章5001 問(wèn)答文章5501 問(wèn)答文章6001 問(wèn)答文章6501 問(wèn)答文章7001 問(wèn)答文章7501 問(wèn)答文章8001 問(wèn)答文章8501 問(wèn)答文章9001 問(wèn)答文章9501
        當(dāng)前位置: 首頁(yè) - 科技 - 知識(shí)百科 - 正文

        對(duì)于Javascript 執(zhí)行上下文的全面了解

        來(lái)源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 22:30:57
        文檔

        對(duì)于Javascript 執(zhí)行上下文的全面了解

        對(duì)于Javascript 執(zhí)行上下文的全面了解:在這篇文章中,將比較深入地闡述下執(zhí)行上下文 – JavaScript中最基礎(chǔ)也是最重要的一個(gè)概念。相信讀完這篇文章后,你就會(huì)明白javascript引擎內(nèi)部在執(zhí)行代碼以前到底做了些什么,為什么某些函數(shù)以及變量在沒(méi)有被聲明以前就可以被使用,以及它們的最終的值是怎樣
        推薦度:
        導(dǎo)讀對(duì)于Javascript 執(zhí)行上下文的全面了解:在這篇文章中,將比較深入地闡述下執(zhí)行上下文 – JavaScript中最基礎(chǔ)也是最重要的一個(gè)概念。相信讀完這篇文章后,你就會(huì)明白javascript引擎內(nèi)部在執(zhí)行代碼以前到底做了些什么,為什么某些函數(shù)以及變量在沒(méi)有被聲明以前就可以被使用,以及它們的最終的值是怎樣

        在這篇文章中,將比較深入地闡述下執(zhí)行上下文 – JavaScript中最基礎(chǔ)也是最重要的一個(gè)概念。相信讀完這篇文章后,你就會(huì)明白javascript引擎內(nèi)部在執(zhí)行代碼以前到底做了些什么,為什么某些函數(shù)以及變量在沒(méi)有被聲明以前就可以被使用,以及它們的最終的值是怎樣被定義的。

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

        Javascript中代碼的運(yùn)行環(huán)境分為以下三種:

        全局級(jí)別的代碼 – 這個(gè)是默認(rèn)的代碼運(yùn)行環(huán)境,一旦代碼被載入,引擎最先進(jìn)入的就是這個(gè)環(huán)境。

        函數(shù)級(jí)別的代碼 – 當(dāng)執(zhí)行一個(gè)函數(shù)時(shí),運(yùn)行函數(shù)體中的代碼。

        Eval的代碼 – 在Eval函數(shù)內(nèi)運(yùn)行的代碼。

        在網(wǎng)上可以找到很多闡述作用域的資源,為了使該文便于大家理解,我們可以將“執(zhí)行上下文”看做當(dāng)前代碼的運(yùn)行環(huán)境或者作用域。下面我們來(lái)看一個(gè)示例,其中包括了全局以及函數(shù)級(jí)別的執(zhí)行上下文:

        上圖中,一共用4個(gè)執(zhí)行上下文。紫色的代表全局的上下文;綠色代表person函數(shù)內(nèi)的上下文;藍(lán)色以及橙色代表person函數(shù)內(nèi)的另外兩個(gè)函數(shù)的上下文。注意,不管什么情況下,只存在一個(gè)全局的上下文,該上下文能被任何其它的上下文所訪問(wèn)到。也就是說(shuō),我們可以在person的上下文中訪問(wèn)到全局上下文中的sayHello變量,當(dāng)然在函數(shù)firstName或者lastName中同樣可以訪問(wèn)到該變量。

        至于函數(shù)上下文的個(gè)數(shù)是沒(méi)有任何限制的,每到調(diào)用執(zhí)行一個(gè)函數(shù)時(shí),引擎就會(huì)自動(dòng)新建出一個(gè)函數(shù)上下文,換句話說(shuō),就是新建一個(gè)局部作用域,可以在該局部作用域中聲明私有變量等,在外部的上下文中是無(wú)法直接訪問(wèn)到該局部作用域內(nèi)的元素的。在上述例子的,內(nèi)部的函數(shù)可以訪問(wèn)到外部上下文中的聲明的變量,反之則行不通。那么,這到底是什么原因呢?引擎內(nèi)部是如何處理的呢?

        執(zhí)行上下文堆棧

        在瀏覽器中,javascript引擎的工作方式是單線程的。也就是說(shuō),某一時(shí)刻只有唯一的一個(gè)事件是被激活處理的,其它的事件被放入隊(duì)列中,等待被處理。下面的示例圖描述了這樣的一個(gè)堆棧:

        我們已經(jīng)知道,當(dāng)javascript代碼文件被瀏覽器載入后,默認(rèn)最先進(jìn)入的是一個(gè)全局的執(zhí)行上下文。當(dāng)在全局上下文中調(diào)用執(zhí)行一個(gè)函數(shù)時(shí),程序流就進(jìn)入該被調(diào)用函數(shù)內(nèi),此時(shí)引擎就會(huì)為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文,并且將其壓入到執(zhí)行上下文堆棧的頂部。瀏覽器總是執(zhí)行當(dāng)前在堆棧頂部的上下文,一旦執(zhí)行完畢,該上下文就會(huì)從堆棧頂部被彈出,然后,進(jìn)入其下的上下文執(zhí)行代碼。這樣,堆棧中的上下文就會(huì)被依次執(zhí)行并且彈出堆棧,直到回到全局的上下文。請(qǐng)看下面一個(gè)例子:

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

        上述foo被聲明后,通過(guò)()運(yùn)算符強(qiáng)制直接運(yùn)行了。函數(shù)代碼就是調(diào)用了其自身3次,每次是局部變量i增加1。每次foo函數(shù)被自身調(diào)用時(shí),就會(huì)有一個(gè)新的執(zhí)行上下文被創(chuàng)建。每當(dāng)一個(gè)上下文執(zhí)行完畢,該上上下文就被彈出堆棧,回到上一個(gè)上下文,直到再次回到全局上下文。真?zhèn)€過(guò)程抽象如下圖:

        由此可見 ,對(duì)于執(zhí)行上下文這個(gè)抽象的概念,可以歸納為以下幾點(diǎn):

        單線程

        同步執(zhí)行

        唯一的一個(gè)全局上下文

        函數(shù)的執(zhí)行上下文的個(gè)數(shù)沒(méi)有限制

        每次某個(gè)函數(shù)被調(diào)用,就會(huì)有個(gè)新的執(zhí)行上下文為其創(chuàng)建,即使是調(diào)用的自身函數(shù),也是如此。

        執(zhí)行上下文的建立過(guò)程

        我們現(xiàn)在已經(jīng)知道,每當(dāng)調(diào)用一個(gè)函數(shù)時(shí),一個(gè)新的執(zhí)行上下文就會(huì)被創(chuàng)建出來(lái)。然而,在javascript引擎內(nèi)部,這個(gè)上下文的創(chuàng)建過(guò)程具體分為兩個(gè)階段:

        建立階段(發(fā)生在當(dāng)調(diào)用一個(gè)函數(shù)時(shí),但是在執(zhí)行函數(shù)體內(nèi)的具體代碼以前)

        建立變量,函數(shù),arguments對(duì)象,參數(shù)

        建立作用域鏈

        確定this的值

        代碼執(zhí)行階段:

        變量賦值,函數(shù)引用,執(zhí)行其它代碼

        實(shí)際上,可以把執(zhí)行上下文看做一個(gè)對(duì)象,其下包含了以上3個(gè)屬性:

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

        建立階段以及代碼執(zhí)行階段的詳細(xì)分析

        確切地說(shuō),執(zhí)行上下文對(duì)象(上述的executionContextObj)是在函數(shù)被調(diào)用時(shí),但是在函數(shù)體被真正執(zhí)行以前所創(chuàng)建的。函數(shù)被調(diào)用時(shí),就是我上述所描述的兩個(gè)階段中的第一個(gè)階段 – 建立階段。這個(gè)時(shí)刻,引擎會(huì)檢查函數(shù)中的參數(shù),聲明的變量以及內(nèi)部函數(shù),然后基于這些信息建立執(zhí)行上下文對(duì)象(executionContextObj)。在這個(gè)階段,variableObject對(duì)象,作用域鏈,以及this所指向的對(duì)象都會(huì)被確定。

        上述第一個(gè)階段的具體過(guò)程如下:

        找到當(dāng)前上下文中的調(diào)用函數(shù)的代碼

        在執(zhí)行被調(diào)用的函數(shù)體中的代碼以前,開始創(chuàng)建執(zhí)行上下文

        進(jìn)入第一個(gè)階段-建立階段:

        建立variableObject對(duì)象:

        建立arguments對(duì)象,檢查當(dāng)前上下文中的參數(shù),建立該對(duì)象下的屬性以及屬性值

        檢查當(dāng)前上下文中的函數(shù)聲明:

        每找到一個(gè)函數(shù)聲明,就在variableObject下面用函數(shù)名建立一個(gè)屬性,屬性值就是指向該函數(shù)在內(nèi)存中的地址的一個(gè)引用

        如果上述函數(shù)名已經(jīng)存在于variableObject下,那么對(duì)應(yīng)的屬性值會(huì)被新的引用所覆蓋。

        初始化作用域鏈

        確定上下文中this的指向?qū)ο?/p>

        代碼執(zhí)行階段:

        執(zhí)行函數(shù)體中的代碼,一行一行地運(yùn)行代碼,給variableObject中的變量屬性賦值。

        下面來(lái)看個(gè)具體的代碼示例:

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

        在調(diào)用foo(22)的時(shí)候,建立階段如下:


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

        由此可見,在建立階段,除了arguments,函數(shù)的聲明,以及參數(shù)被賦予了具體的屬性值,其它的變量屬性默認(rèn)的都是undefined。一旦上述建立階段結(jié)束,引擎就會(huì)進(jìn)入代碼執(zhí)行階段,這個(gè)階段完成后,上述執(zhí)行上下文對(duì)象如下:

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

        我們看到,只有在代碼執(zhí)行階段,變量屬性才會(huì)被賦予具體的值。

        局部變量作用域提升的緣由

        在網(wǎng)上一直看到這樣的總結(jié): 在函數(shù)中聲明的變量以及函數(shù),其作用域提升到函數(shù)頂部,換句話說(shuō),就是一進(jìn)入函數(shù)體,就可以訪問(wèn)到其中聲明的變量以及函數(shù)。這是對(duì)的,但是知道其中的緣由嗎?相信你通過(guò)上述的解釋應(yīng)該也有所明白了。不過(guò)在這邊再分析一下。

        看下面一段代碼:

         (function() {
         console.log(typeof foo); // function pointer
         console.log(typeof bar); // undefined
         
         var foo = 'hello',
         bar = function() {
         return 'world';
         };
         
         function foo() {
         return 'hello';
         }
         
         }());​

        上述代碼定義了一個(gè)匿名函數(shù),并且通過(guò)()運(yùn)算符強(qiáng)制理解執(zhí)行。那么我們知道這個(gè)時(shí)候就會(huì)有個(gè)執(zhí)行上下文被創(chuàng)建,我們看到例子中馬上可以訪問(wèn)foo以及bar變量,并且通過(guò)typeof輸出foo為一個(gè)函數(shù)引用,bar為undefined。

        為什么我們可以在聲明foo變量以前就可以訪問(wèn)到foo呢?

        因?yàn)樵谏舷挛牡慕㈦A段,先是處理arguments, 參數(shù),接著是函數(shù)的聲明,最后是變量的聲明。那么,發(fā)現(xiàn)foo函數(shù)的聲明后,就會(huì)在variableObject下面建立一個(gè)foo屬性,其值是一個(gè)指向函數(shù)的引用。當(dāng)處理變量聲明的時(shí)候,發(fā)現(xiàn)有var foo的聲明,但是variableObject已經(jīng)具有了foo屬性,所以直接跳過(guò)。當(dāng)進(jìn)入代碼執(zhí)行階段的時(shí)候,就可以通過(guò)訪問(wèn)到foo屬性了,因?yàn)樗呀?jīng)就存在,并且是一個(gè)函數(shù)引用。

        為什么bar是undefined呢?

        因?yàn)閎ar是變量的聲明,在建立階段的時(shí)候,被賦予的默認(rèn)的值為undefined。由于它只要在代碼執(zhí)行階段才會(huì)被賦予具體的值,所以,當(dāng)調(diào)用typeof(bar)的時(shí)候輸出的值為undefined。

        好了,到此為止,相信你應(yīng)該對(duì)執(zhí)行上下文有所理解了,這個(gè)執(zhí)行上下文的概念非常重要,務(wù)必好好搞懂之!

        以上這篇對(duì)于Javascript 執(zhí)行上下文的全面了解就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

        聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

        文檔

        對(duì)于Javascript 執(zhí)行上下文的全面了解

        對(duì)于Javascript 執(zhí)行上下文的全面了解:在這篇文章中,將比較深入地闡述下執(zhí)行上下文 – JavaScript中最基礎(chǔ)也是最重要的一個(gè)概念。相信讀完這篇文章后,你就會(huì)明白javascript引擎內(nèi)部在執(zhí)行代碼以前到底做了些什么,為什么某些函數(shù)以及變量在沒(méi)有被聲明以前就可以被使用,以及它們的最終的值是怎樣
        推薦度:
        標(biāo)簽: js 了解 javascript
        • 熱門焦點(diǎn)

        最新推薦

        猜你喜歡

        熱門推薦

        專題
        Top
        主站蜘蛛池模板: 亚洲无人区码一二三码区别图片 | 亚洲JIZZJIZZ妇女| 最近免费中文字幕大全高清大全1| 国产亚洲综合一区柠檬导航| 朝桐光亚洲专区在线中文字幕 | 国产亚洲精品资在线| 免费看一级毛片在线观看精品视频| 日本不卡视频免费| 美女视频黄频a免费| 亚洲男人天堂2020| 国产免费阿v精品视频网址| 久久久影院亚洲精品| 91人成网站色www免费下载| 亚洲一卡二卡三卡四卡无卡麻豆| 性xxxxx免费视频播放| 国产日本亚洲一区二区三区 | 国产成人午夜精品免费视频| 亚洲影视自拍揄拍愉拍| 最近免费中文字幕大全视频| 国产精品国产亚洲区艳妇糸列短篇| 免费看又爽又黄禁片视频1000| 国产成人综合亚洲一区| 国产亚洲人成A在线V网站| 午夜免费啪视频在线观看| 亚洲91精品麻豆国产系列在线| 日韩中文字幕在线免费观看 | 怡红院亚洲红怡院在线观看| 亚洲色偷拍区另类无码专区| 国内精品一级毛片免费看| 亚洲国产精品成人精品软件| 日本不卡在线观看免费v| 国产精品黄页免费高清在线观看| 亚洲AV午夜成人影院老师机影院| 无码国产精品一区二区免费| 在线91精品亚洲网站精品成人| 亚洲老妈激情一区二区三区| 真人做A免费观看| 一级毛片在线免费播放| 亚洲美女人黄网成人女| 又爽又黄无遮挡高清免费视频 | 免费无码成人AV片在线在线播放|