欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

js 執(zhí)行上下文和作用域的相關(guān)總結(jié)

 更新時(shí)間:2021年02月08日 11:56:26   作者:前端Serendipity  
這篇文章主要介紹了js 執(zhí)行上下文和作用域的相關(guān)知識(shí)總結(jié),幫助大家更好的理解和使用JavaScript,感興趣的朋友可以了解下

前言

  如果你是或者你想成為一名合格的前端開(kāi)發(fā)工作者,你必須知道JavaScript代碼在執(zhí)行過(guò)程,知道執(zhí)行上下文、作用域、變量提升等相關(guān)概念,并且熟練應(yīng)用到自己的代碼中。本文參考了你不知道的JavaScript,和JavaScript高級(jí)程序設(shè)計(jì),以及部分博客。

正文

     1.JavaScript代碼的執(zhí)行過(guò)程相關(guān)概念

  js代碼的執(zhí)行分為編譯器的編譯和js引擎與作用域執(zhí)行兩個(gè)階段,其中編譯器編譯的階段(預(yù)編譯階段)分為分詞/詞法分析、解析/語(yǔ)法分析、代碼生成三個(gè)階段?!     ?/p>

     (1)在分詞/詞法分析階段,編譯器負(fù)責(zé)將代碼進(jìn)行分割處理,將語(yǔ)句分割成詞法單元流/數(shù)組;

    ?。?)在解析/詞法分析階段,將上一階段的詞法單元流轉(zhuǎn)換成由元素嵌套組成的符合程序語(yǔ)法結(jié)構(gòu)的抽象語(yǔ)法樹;

     (3)在代碼生成階段,將抽象語(yǔ)法樹轉(zhuǎn)換成可執(zhí)行代碼,并交付給js引擎。

   js代碼執(zhí)行的三個(gè)重要角色:

   ?。?)js引擎:負(fù)責(zé)代碼執(zhí)行的整個(gè)過(guò)程

    (2)編譯器:負(fù)責(zé)js代碼語(yǔ)法解析和生成可執(zhí)行代碼

   ?。?)作用域:手機(jī)并維護(hù)所有聲明標(biāo)識(shí)符,根據(jù)特定規(guī)則確定當(dāng)前代碼對(duì)聲明的標(biāo)識(shí)符的訪問(wèn)權(quán)限

   2. 執(zhí)行上下文和執(zhí)行棧

  每當(dāng)js代碼在運(yùn)行的時(shí)候,它都是在執(zhí)行上下文中運(yùn)行。說(shuō)到執(zhí)行上下文,需要知道什么時(shí)執(zhí)行棧,執(zhí)行棧,就是其他編程語(yǔ)言中的“調(diào)用?!?,是一種擁有LIFO(后進(jìn)先出)數(shù)據(jù)結(jié)構(gòu)的棧,被用來(lái)存儲(chǔ)代碼運(yùn)行時(shí)所創(chuàng)建的執(zhí)行上下文。當(dāng)js引擎第一次遇到要執(zhí)行的代碼的時(shí)候,首先會(huì)創(chuàng)建一個(gè)全局的執(zhí)行上下文并壓入當(dāng)前執(zhí)行棧,每當(dāng)引擎遇到一個(gè)函數(shù)調(diào)用,它會(huì)為該函數(shù)創(chuàng)建一個(gè)新的執(zhí)行上下文并壓入棧頂,js引擎執(zhí)行棧頂?shù)暮瘮?shù),當(dāng)該函數(shù)執(zhí)行完畢,執(zhí)行上下文從棧中彈出,控制流程到達(dá)下一個(gè)上下文。對(duì)于每一個(gè)執(zhí)行上下文都含有三個(gè)重要屬性:變量對(duì)象,作用域鏈,this。這些屬性也需要徹底理解。

  2.1 、上下文調(diào)用棧

var scope1 = "global scope";
 function checkscope1(){
 var scope1 = "local scope";
 function f(){
 console.log(scope1); 
 }
 return f();
 }
 checkscope1();
var scope2 = "global scope";
 function checkscope2(){
 var scope2 = "local scope";
 function f(){
 console.log(scope2);
 }
 return f;
 }
 checkscope2()();

  上面兩段代碼都會(huì)輸出 local scope

    上面代碼中scope一定是局部變量,查找塊級(jí)作用域即可,不管何時(shí)何地執(zhí)行 f(),這種綁定在執(zhí)行f()時(shí)依然有效。出現(xiàn)了一樣的結(jié)果,但是兩段代碼的執(zhí)行上下文棧的變化不一樣 :

  第一段代碼:push(<checkscope1>functionContext)=>push(<f>functionContext)=>pop()=>pop()

    第二段代碼:push(<checkscope2>functionContext)=>pop()=>push(<f>functionContext)=>pop()

  2.2 、三種執(zhí)行上下文類型

 ?。?)全局上下文

    js引擎開(kāi)始解析js代碼的時(shí)候首先遇到的就是全局代碼,初始化的時(shí)候會(huì)在調(diào)用棧中壓入一個(gè)全局執(zhí)行的上下文,當(dāng)整個(gè)應(yīng)用程序結(jié)束的時(shí)候才會(huì)清空?qǐng)?zhí)行上下文棧,棧的最底部永遠(yuǎn)時(shí)全局執(zhí)行上下文。這是默認(rèn)的或者說(shuō)基礎(chǔ)的全局作用域,任何函數(shù)內(nèi)部的代碼都在全局作用域中,首先創(chuàng)建一個(gè)全局的window對(duì)象,然后設(shè)置this的值等于這個(gè)全局對(duì)象,一個(gè)程序中只有一個(gè)全局執(zhí)行上下文。在頂層js代碼中可以使用this引用全局對(duì)象,因?yàn)槿謱?duì)象時(shí)是域鏈的頭,意味著所有非限定性的變量和函數(shù)都作為該對(duì)象的函數(shù)來(lái)查詢。

    總之,全局執(zhí)行上下文只有一個(gè),在客戶端中一般由瀏覽器創(chuàng)建,也就是我們熟知的window對(duì)象,我們能通過(guò)this直接訪問(wèn)到它。

 ?。?)函數(shù)上下文

    每當(dāng)一個(gè)函數(shù)被調(diào)用是,都會(huì)外該函數(shù)創(chuàng)建一個(gè)新的上下文,每個(gè)函數(shù)都擁有自己的上下文,不過(guò)是在函數(shù)調(diào)用的時(shí)候創(chuàng)建的,需要注意的是同一個(gè)函數(shù)被多次調(diào)用,都會(huì)創(chuàng)建一個(gè)新的上下文。

  (3)eval和with上下文

    執(zhí)行在 eval和with 函數(shù)內(nèi)部的代碼也會(huì)有它屬于自己的執(zhí)行上下文,但由于 JavaScript 開(kāi)發(fā)者并不經(jīng)常使用 eval,所以在這里我不會(huì)討論它。

  2.3 、執(zhí)行上下文創(chuàng)建階段

    執(zhí)行上下文創(chuàng)建分為創(chuàng)建階段與執(zhí)行階段兩個(gè)階段

    js引擎在執(zhí)行上下文創(chuàng)建階段主要負(fù)責(zé)三件事:確定this==>創(chuàng)建詞法環(huán)境組件==>創(chuàng)建變量環(huán)境組件(目前還不太理解)

    (1)確定this,這個(gè)不做詳解

   ?。?)創(chuàng)建詞法環(huán)境組件

    詞法環(huán)境是一種規(guī)范類型,基于 ECMAScript 代碼的詞法嵌套結(jié)構(gòu)來(lái)定義標(biāo)識(shí)符和具體變量和函數(shù)的關(guān)聯(lián)。一個(gè)詞法環(huán)境由環(huán)境記錄器和一個(gè)可能的引用外部詞法環(huán)境的空值組成。其中環(huán)境記錄用于存儲(chǔ)當(dāng)前環(huán)境中的變量和函數(shù)聲明的實(shí)際位置;外部環(huán)境引入記錄很好理解,它用于保存自身環(huán)境可以訪問(wèn)的其它外部環(huán)境,那么說(shuō)到這個(gè),是不是有點(diǎn)作用域鏈的意思?

    詞法環(huán)境有兩種類型:

  • 全局環(huán)境(在全局執(zhí)行上下文中)是沒(méi)有外部環(huán)境引用的詞法環(huán)境。全局環(huán)境的外部環(huán)境引用是 null。它擁有內(nèi)建的 Object/Array/等、在環(huán)境記錄器內(nèi)的原型函數(shù)(關(guān)聯(lián)全局對(duì)象,比如 window 對(duì)象)還有任何用戶定義的全局變量,并且 this的值指向全局對(duì)象。
  • 在函數(shù)環(huán)境中,函數(shù)內(nèi)部用戶定義的變量存儲(chǔ)在環(huán)境記錄器中。并且引用的外部環(huán)境可能是全局環(huán)境,或者任何包含此內(nèi)部函數(shù)的外部函數(shù)。

   ?。?)創(chuàng)建變量環(huán)境組件

    變量環(huán)境可以說(shuō)也是詞法環(huán)境,它具備詞法環(huán)境所有屬性,一樣有環(huán)境記錄與外部環(huán)境引入。在ES6中唯一的區(qū)別在于詞法環(huán)境用于存儲(chǔ)函數(shù)聲明與let const聲明的變量,而變量環(huán)境僅僅存儲(chǔ)var聲明的變量。

   3. JavaScript作用域和作用域鏈

  3.1、作用域

  詞法作用域是在寫代碼或者定義的時(shí)候確定的,而動(dòng)態(tài)作用域是在運(yùn)行時(shí)確定的,(this也是)詞法作用域關(guān)注函數(shù)在何處聲明,而動(dòng)態(tài)作用域關(guān)注函數(shù)從何處調(diào)用,JavaScript采用詞法作用域,其作用域由你在寫代碼是將變量和塊作用域?qū)懺谀睦餂Q定,因此當(dāng)詞法分析器處理代碼時(shí)會(huì)保持作用域不變??梢岳斫鉃樽饔糜蚓褪且粋€(gè)獨(dú)立的地盤,讓變量不會(huì)外泄、暴露出去。也就是說(shuō)作用域最大的用處就是隔離變量,不同作用域下同名變量不會(huì)有沖突。

  理解作用域之前先來(lái)看一道題

function foo() {
 console.log(value);
 }
 var value = 1;
 function bar() {
 var value = 2;
 console.log(value);
 foo();
 }
 bar();

  上面的代碼會(huì)輸出什么呢,首先在全局上下文中聲明foo()函數(shù)、value變量(其值為undefined)、bar()函數(shù),代碼執(zhí)行階段,bar函數(shù)上下文入棧并執(zhí)行,打印出value為2,然后執(zhí)行foo(),foo()入棧,打印value時(shí)找不到該變量,js引擎會(huì)查找上層作用域,即全局作用域,于是打印出1。后面函數(shù)執(zhí)行完畢上下文出棧。再來(lái)看下面這個(gè)函數(shù),作用域是分層的,內(nèi)層作用域可以訪問(wèn)外層作用域的變量,反之則不行。

  ES6以來(lái),js中的作用域分為全局作用域,函數(shù)作用域,塊級(jí)作用域和欺騙作用域。

  3.1.1、全局作用域

  在代碼中任何地方都能訪問(wèn)到的對(duì)象擁有全局作用域,最外層函數(shù)和在最外層函數(shù)外面定義的變量擁有全局作用域,所有末定義直接賦值的變量自動(dòng)聲明為擁有全局作用域。

  3.1.2、函數(shù)作用域

  函數(shù)作用域的含義是指,屬于這個(gè)函數(shù)的全部變量都可以在整個(gè)函數(shù)的范圍內(nèi)使用及復(fù)用(事實(shí)上在嵌套的作用域中也可以使用);
      這個(gè)原則是指在軟件設(shè)計(jì)中,應(yīng)該最小限度地暴露必 要內(nèi)容,而將其他內(nèi)容都“隱藏”起來(lái);
      函數(shù)表達(dá)式可以是匿名的, 而函數(shù)聲明則不可以省略函數(shù)名。
  3.1.3、塊作用域
  塊作用域,通常指 { .. } 內(nèi)部
        (1)if 、 try/catch創(chuàng)建塊作用域;
        (2)let 關(guān)鍵字可以將變量綁定到所在的任意作用域中(通常是 { .. } 內(nèi)部);
        (3)for 循環(huán)頭部的 let 不僅將 i 綁定到了 for 循環(huán)的塊中,事實(shí)上它將其重新綁定到了循環(huán)的每一個(gè)迭代中,確保使用上一個(gè)循環(huán)迭代結(jié)束時(shí)的值重新進(jìn)行賦值;
        (4)const同樣可以用來(lái)創(chuàng)建塊作用域變量,但其值是固定的 (常量)。創(chuàng)建對(duì)象時(shí)值可以被改變。
  3.1.4、欺騙詞法作用域的方法,eval()和with()
    eval()參數(shù)為一個(gè)字符串,并把里面的內(nèi)容當(dāng)作書寫在該位置的代碼一樣處理(非嚴(yán)格模式);
   with()當(dāng)需要重復(fù)引用一個(gè)對(duì)象的多個(gè)屬性時(shí),可以不需要重復(fù)引用對(duì)象本身。

  3.2、作用域鏈

    作用域鏈本質(zhì)上就是根據(jù)名稱查找變量(標(biāo)識(shí)符名稱)的一套規(guī)則。規(guī)則非常簡(jiǎn)單,在自己的變量對(duì)象里找不到變量,就上父級(jí)的變量對(duì)象查找,當(dāng)?shù)诌_(dá)最外層的全局上下文中,無(wú)論找到還是沒(méi)找到,查找過(guò)程都會(huì)停止。查找會(huì)在找到第一個(gè)匹配的變量時(shí)停止,被稱為遮蔽效應(yīng)

   作用域鏈的用途是保證對(duì)執(zhí)行環(huán)境有權(quán)訪問(wèn)的所有變量和函數(shù)的有序訪問(wèn)                              
        作用域鏈:當(dāng)函數(shù)定義時(shí),系統(tǒng)生成([scope])屬性,該屬性保存該函數(shù)的作用域鏈,該作用域鏈的第0位存儲(chǔ)當(dāng)前環(huán)境下的全局執(zhí)行期上下文GO,GO里存儲(chǔ)全局下的所有對(duì)象,其中包含函數(shù)和全局變量,當(dāng)函數(shù)執(zhí)行的前一刻,預(yù)編譯的時(shí)候,作用域鏈的頂端(第0位)存儲(chǔ)函數(shù)生成的執(zhí)行上下文AO,同時(shí)第一位存儲(chǔ)GO
        查找變量是到函數(shù)存儲(chǔ)的作用域鏈中從頂端開(kāi)始依次向下查找(函數(shù)內(nèi)部作用域在最頂端,證明了函數(shù)可以訪問(wèn)外部的變量,而外部無(wú)法訪問(wèn)函數(shù)內(nèi)部的變量)

  4.執(zhí)行上下文和作用域的區(qū)別

  每個(gè)函數(shù)調(diào)用都有與之相關(guān)的作用域和上下文。從根本上說(shuō),范圍是基于函數(shù)(function-based)而上下文是基于對(duì)象(object-based)。換句話說(shuō), 作用域是和每次函數(shù)調(diào)用時(shí)變量的訪問(wèn)有關(guān),并且每次調(diào)用都是獨(dú)立的。上下文總是關(guān)鍵字 this 的值,是調(diào)用當(dāng)前可執(zhí)行代碼的對(duì)象的引用。作用域是函數(shù)定義的時(shí)候就確定好的了,函數(shù)當(dāng)中的變量是和函數(shù)所處的作用域有關(guān),函數(shù)運(yùn)行的作用域也是與該函數(shù)定義時(shí)的作用域有關(guān)。而上下文,主要是關(guān)鍵字this的值,這個(gè)是由函數(shù)運(yùn)行時(shí)決定的,簡(jiǎn)單來(lái)說(shuō)就是誰(shuí)調(diào)用此函數(shù),this就指向誰(shuí)。

  5.最后

以上就是js 執(zhí)行上下文和作用域的相關(guān)總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于js 執(zhí)行上下文和作用域的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論