作用域是什么宿稀?
思維導圖
編譯原理
我們都認為js是一門動態(tài)語言祝沸,但是其實是一門編譯語言越庇,但是不是提前編譯的卤唉,編譯結(jié)果也不能在分布式系 統(tǒng)中進行移植
編譯流程
傳統(tǒng)編譯語言的流程
程序中的一段源代碼在執(zhí)行之前會經(jīng)歷三個步驟桑驱,統(tǒng)稱為“編譯”。
分別是:分詞/詞法分析萍聊,解析/語法分析寿桨,代碼生成亭螟。
1. 分詞和詞法分析:
詞法單元:
將由字符組成的字符串分解成(對編程語言來說)有意義的代碼塊预烙,這些代碼塊被稱為詞法單元扁掸。
分詞和詞法分析的主要差異:
主要差異在于詞法單元的識別是通過有狀態(tài)還是無狀態(tài)的方式進行的主要差異在于詞法單元的識別是通過有狀態(tài)還是無狀態(tài)的方式進行的谴分。
2.解析/語法分析
將詞法單元流轉(zhuǎn)換為抽象的語法樹牺蹄。這個樹叫做抽象語法樹(AST)沙兰。
3.代碼生成
將 AST 轉(zhuǎn)換為可執(zhí)行代碼的過程稱被稱為代碼生成
js編譯發(fā)生的時間節(jié)點
JavaScript 的編譯過程不是發(fā)生在構(gòu)建之前的。大部分情況下編譯發(fā)生在代碼執(zhí)行前的幾微秒的時間內(nèi)鼎天。在編譯時舀奶,js引擎會盡力進行性能優(yōu)化。
理解作用域
角色
引擎
從頭到尾負責整個 JavaScript 程序的編譯及執(zhí)行過程训措。
編譯器
負責語法分析及代碼生成
作用域
負責收集并維護由所有聲明的標識符(變量)組成的一系列查詢伪节,并實施一套非常嚴格的規(guī)則光羞,確定當前執(zhí)行的代碼對這些標識符的訪問權(quán)限绩鸣。
var a = 2
其實var a = 2
是執(zhí)行了2個聲明。分別在編譯器和引擎中纱兑。
首先編譯器會執(zhí)行正常的編譯呀闻,也就是詞法分析,語法分析潜慎。在最后的代碼生產(chǎn)中就會和作用域“交流”捡多。對變量進行聲明。
而a = 2
這個賦值操作就是在引擎運行時進行的铐炫,變量是通過作用域進行查找垒手,如果最后沒找到就會報錯。
總結(jié):變量的賦值操作會執(zhí)行兩個動作倒信,首先編譯器會在當前作用域中聲明一個變量(如 果之前沒有聲明過)科贬,然后在運行時引擎會在作用域中查找該變量,如果能夠找到就會對 它賦值鳖悠。
LHS和RHS查詢
引擎對變量的查找分為LHS和RHS2種榜掌。
LHS 查詢則是試圖 找到變量的容器本身,從而可以對其賦值乘综。
RHS則是取到變量的源值憎账。
LHS 和 RHS 的含義是“賦值操作的左側(cè)或右側(cè)”不是“=”
特別注意:函數(shù)聲明
編譯器可以在代碼生成的同時處理聲明和值的定義,比如在引擎執(zhí)行代碼時卡辰,并不會有線程專門用來將一個函數(shù)值“分配給” 一個變量胞皱。在編譯器聲明階段函數(shù)就已經(jīng)賦值了。
引擎和作用域的對話
1.小案例
2.小案例
作用域嵌套
作用域是像一個氣泡一樣九妈,一個大氣泡包裹一個小氣泡反砌,小氣泡里面找不到的變量就會去大氣泡里面找,逐級向上的查找允蚣。
當一個塊或函數(shù)嵌套在另一個塊或函數(shù)中時于颖,就發(fā)生了作用域的嵌套。因此嚷兔,在當前作用 域中無法找到某個變量時森渐,引擎就會在外層嵌套的作用域中繼續(xù)查找做入,直到找到該變量, 或抵達最外層的作用域(也就是全局作用域)為止同衣。
遍歷嵌套作用域鏈的規(guī)則
引擎從當前的執(zhí)行作用域開始查找變量竟块,如果找不到, 就向上一級繼續(xù)查找耐齐。當?shù)诌_最外層的全局作用域時浪秘,無論找到還是沒找到,查找過程都 會停止埠况。
異常
RHS 查詢耸携,找不到就報錯
RHS 查詢在所有嵌套的作用域中遍尋不到所需的變量,引擎就會拋出 ReferenceError 異常辕翰。
LHS查詢找不到就聲明一個
當引擎執(zhí)行 LHS 查詢時夺衍,如果在頂層(全局作用域)中也無法找到目標變量, 全局作用域中就會創(chuàng)建一個具有該名稱的變量喜命,并將其返還給引擎(非嚴格模式)沟沙,嚴格模式依然報錯。
對變量亂操作
引擎會拋出另外一種類型的異常壁榕,叫作 TypeError矛紫。
undefined.xxx
,var a = 3; a()
這就是典型的濫用。
總結(jié):
ReferenceError 同作用域判別失敗相關(guān)牌里,而 TypeError 則代表作用域判別成功了颊咬,但是對 結(jié)果的操作是非法或不合理的。
參考
你不知道的js(上)二庵,其實就是讀書筆記贪染,哈哈哈。
本文由mdnice多平臺發(fā)布