你不知道的JavaScript(一)|作用域和閉包

關(guān)于在我的前端文集中發(fā)表的你不知道的JavaScript(xx)|xx,是我在閱讀《你不知道的JavaScript》時對知識點的摘抄。

編譯原理
在傳統(tǒng)編譯語言的流程中,程序中的一段源代碼在執(zhí)行之前會經(jīng)歷三個步驟,統(tǒng)稱為“編譯”。
1、分詞/詞法分析
2、解析/語法分析
3、代碼生成
比起那些編譯過程只有三個步驟的語言的編譯器,JavaScript引擎要復(fù)雜得多。例如,在語法分析和代碼生成階段有特定的步驟來對運(yùn)行性能進(jìn)行優(yōu)化,包括對冗余元素進(jìn)行優(yōu)化等。
首先,JavaScript引擎不會有大量的(像其他語言編譯器那么多的)時間用來進(jìn)行優(yōu)化,因為與其他語言不同,JavaScript的編譯過程不是發(fā)生在構(gòu)建之前的。
對于JavaScript來說,大部分情況下編譯發(fā)生在代碼執(zhí)行前的幾微秒(甚至更短!)的時間內(nèi)。在我們所要討論的作用域背后,JavaScript引擎用盡了各種辦法(比如JIT,可以延遲編譯甚至實施重編譯)來保證性能最佳。
簡單地說,任何JavaScript代碼片段在執(zhí)行前都要進(jìn)行編譯(通常就在執(zhí)行前)。因此,JavaScript編譯器首先會對var a = 2;這段程序進(jìn)行編譯,然后做好執(zhí)行它的準(zhǔn)備,并且通常馬上就會執(zhí)行它。

理解作用域
1、引擎
從頭到尾負(fù)責(zé)整個JavaScript程序的編譯及執(zhí)行過程。
2、編譯器
引擎的好朋友之一,負(fù)責(zé)語法分析及代碼生成等。
3、作用域
引擎的另一位好朋友,負(fù)責(zé)收集并維護(hù)由所有聲明的標(biāo)示符(變量)組成的一系列查詢,并實施一套非常嚴(yán)格的規(guī)則,確定當(dāng)前執(zhí)行的代碼對這些標(biāo)示符的訪問權(quán)限。
總結(jié):變量的賦值操作會執(zhí)行兩個動作,首先編譯器會在當(dāng)前作用域中聲明一個變量(如果之前沒有聲明過),然后在運(yùn)行時引擎會在作用域中查找該變量,如果能夠找到就會對它賦值。
為了進(jìn)一步理解,我們需要多介紹一點編譯器的術(shù)語。
編譯器在編譯過程的第二步中生成了代碼,引擎執(zhí)行它時,會通過查找變量a來判斷它是否已聲明過。查找的過程由作用域進(jìn)行協(xié)助,但是引擎執(zhí)行怎樣的查找,會影響最終的查找結(jié)果。
LHS(左側(cè)查找):賦值操作的目標(biāo)是誰。
RHS(右側(cè)查找):誰是賦值操作的源頭。

function foo(a) {
    console.log( a ); // 2
}
foo( 2 );

以上代碼,[1]最后一行foo(..)函數(shù)的調(diào)用需要對foo進(jìn)行RHS引用,[2]代碼中隱式的a=2操作發(fā)生在2被當(dāng)做參數(shù)傳遞給foo(..)函數(shù)時,2會被分配給參數(shù)a。為了給參數(shù)a(隱式地)分配值,需要進(jìn)行一次LHS查詢。[3]這里還有對a進(jìn)行的RHS引用,并且將得到的值傳給了console.log(..)。console.log(..)本身也需要一個引用才能執(zhí)行,因此會對console對象進(jìn)行RHS查詢,并且檢查得到的值中是否有一個叫做log的方法。

作用域嵌套
當(dāng)一個塊或函數(shù)嵌套在另一個塊或函數(shù)中時,就發(fā)生了作用域的嵌套。因此,在當(dāng)前作用域中無法找到某個變量時,引擎就會在外層嵌套的作用域中繼續(xù)查找,直到找到該變量,或抵達(dá)最外層的作用域(也就是全局作用域)為止。

異常
如果RHS查詢在所有嵌套的作用域中找不到所需的變量,引擎就會拋出ReferenceError異常。值得注意的是,ReferenceError是非常重要的異常類型。
相比較之下,當(dāng)引擎執(zhí)行LHS查詢時,如果在頂層(全局作用域)中也無法找到目標(biāo)變量,全局作用域中就會創(chuàng)建一個具有該名稱的變量,并將其返還給引擎,前提是程序運(yùn)行在非“嚴(yán)格模式”下。
ES5中引入了“嚴(yán)格模式”。同正常模式,或者說寬松/懶惰模式相比,嚴(yán)格模式在行為上有很多不同。其中一個不同的行為是嚴(yán)格模式禁止自動或隱式地創(chuàng)建全局變量。因此,在嚴(yán)格模式中LHS查詢失敗時,并不會創(chuàng)建并返回一個全局變量,引擎會拋出同RHS查詢失敗時類似的ReferenceError異常。
接下來,如果RHS查詢找到了一個變量,但是你嘗試對這個變量的值進(jìn)行不合理的操作,比如試圖對一個非函數(shù)類型的值進(jìn)行函數(shù)調(diào)用,或者引用null或undefined類型的值得屬性,那么引擎會拋出另外一種類型的異常,叫做
TypeError。
ReferenceError同作用域判別失敗相關(guān),而TypeError則代表作用域判別成功了,但是對結(jié)果的操作是非法或不合理的。

我于高山之上兮,望我故鄉(xiāng).故鄉(xiāng)不可見兮,永不能忘.
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容