您好,登錄后才能下訂單哦!
這篇文章主要講解了“JavaScript作用域的作用是什么”,文中的講解內(nèi)容簡單清晰,易于學(xué)習(xí)與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學(xué)習(xí)“JavaScript作用域的作用是什么”吧!
作用域的概念
現(xiàn)代編程語言的最基本功能之一就是能夠存儲變量當中的值,以便于之后的使用于修改。也正是這個功能將狀態(tài)帶給了程序。
在JavaScript中,作用域就是一套設(shè)計良好的規(guī)則來存儲變量。
簡述編譯原理
通常我們會將JavaScript歸類為“動態(tài)”或“解釋執(zhí)行“語言,但它實際上是一門編譯語言。與傳統(tǒng)的編譯語言不同,它不是提前編譯的,編譯結(jié)果也不能在分布式系統(tǒng)中進行移植。
例如V8引擎,為了提高JavaScript代碼的運行性能,在運行之前會先將其編譯為本地的機器碼,然后再去執(zhí)行機器碼,達到提升速度的目的。
分詞/詞法分析
這個過程將由字符組成的代碼分解成對程序有意義的代碼塊,這些代碼塊被稱為詞法單元。
例如 var foo = 'bar' 通常會被分解為這些詞法單元:var 、 foo 、 = 、 'bar'
解析/語法分析
這個過程將詞法單元轉(zhuǎn)換成一個“由元素逐級嵌套組成的代表程序語法的樹“,這個樹被稱為“抽象語法樹”(AST)。
image
代碼生成
將上邊的抽象語法樹轉(zhuǎn)換為機器可執(zhí)行代碼
JavaScript引擎比只有三個步驟的語言的編譯器要復(fù)雜的多。例如在語法分析和代碼生成階段有特定的步驟來對運行性能進行優(yōu)化,包括對冗余元素進行優(yōu)化等。
對于JavaScript來說,大部分情況下編譯發(fā)生在代碼執(zhí)行的前幾微秒,任何代碼片段在執(zhí)行前都要進行編譯。因此JavaScript編譯器首先對 var foo = 'bar' 進行編譯,然后做好執(zhí)行它的準備,并且通常馬上就會執(zhí)行它。
引擎、編譯器、作用域在賦值操作中的配合
引擎:從頭到尾負責整個JavaScript程序編譯及執(zhí)行過程
編譯器:負責語法分析及代碼生成
作用域:負責收集維護由所有變量組成的一系列查詢
對于 var foo = 'bar' 這段代碼,大家很有可能認為是一句簡單的聲明。而事實上JavaScript執(zhí)行時會將它分成兩個完全不同的聲明。
編譯器首先將這段代碼分解成詞法單元,然后解析為樹結(jié)構(gòu)。(在下一步代碼生成時,處理這段代碼的方式會跟預(yù)期有所不同)
遇到 var foo ,編譯器會檢查作用域是否已有同名變量存在。如果有的話編譯器會忽略聲明,繼續(xù)編譯。否則它會生成代碼在當前作用域的變量集合中聲明一個新的變量,命名為 foo
接下來編譯器會為引擎生成運行時所需代碼,用來處理 foo = 'bar' 這個賦值操作。
引擎運行時會首先查詢當前作用域是否存在叫做 foo 的變量。如果有引擎則會使用這個變量,否則會一直向上層作用域查找。
如果最終找到了 foo 這個變量,就會將 'bar' 賦給它,否則拋出異常。
總結(jié):變量的賦值會執(zhí)行兩個動作:首先是編譯器在當前作用域中聲明變量(如果變量未被聲明過);接著運行時引擎在作用域查找該變量,能找到就會對它賦值。
LHS查詢 vs RHS查詢
引擎執(zhí)行編譯器生成的代碼時,會通過查找 foo 來判斷是否已經(jīng)聲明過。查找的過程由作用域來協(xié)助。在我們的例子中,引擎為變量 foo 進行的時LHS查詢,還有另一個查找類型叫RHS查詢。顧名思義,它們的意思是Left hand side 和 Right hand side
LHS:變量出現(xiàn)在賦值操作的左側(cè)(查找賦值操作的目標是誰)
RHS:變量在其他位置出現(xiàn)(查找值的源頭)
// 考慮下邊的代碼 console.log(foo)
此例中 foo 的引用就是RHS查詢,這里沒有賦予 foo 任何值,相反的,我們需要查找 foo 的值,才能傳遞給log方法。
// 相比之下 foo = 'bar'
這里對 foo 的查詢則是LHS查詢,我們并不關(guān)心 foo 當前的值是什么, 只是想為這個賦值操作找到目標。
// 再分析下邊的代碼 function foo(a) { console.log(a) } foo('bar')
這段代碼里既有LHS查詢又有RHS查詢
最后一行 foo(...) 函數(shù)的調(diào)用需要對 foo 進行RHS查詢 → 找到 foo 的值
入?yún)r存在隱式的 a = 'bar' ,需要對 a 進行LHS查詢
console.log(a) 對 a 進行RHS查詢
console.log(...) 本身也需要對 console 對象進行RHS查詢
作用域的嵌套
我們在文章開始時說過,作用域是根據(jù)名稱查找變量的一套規(guī)則。實際情況中需要同時顧及幾個作用域。
當一個塊或函數(shù)嵌套在另一個塊或函數(shù)中時,就發(fā)生了作用域的嵌套。因此在當前作用域中沒有查找到目標變量時,會逐層向上查找直到全局作用域。
// 考慮以下代碼 function foo(a) { console.log(a + b) } var b = 258; foo(369)
對 b 進行的RHS查詢無法在 foo 內(nèi)部完成,但可以在上一級的作用域中完成(在此例中是全局作用域)。
LHS,RHS查詢都會在作用域內(nèi)逐層查找,直到找到為止(或到達全局作用域)。
ReferenceError
上一節(jié)提到了LHS,RHS都會在作用域內(nèi)逐層查找變量,但如果到達全局作用域仍然沒有找到變量怎么辦呢?
這時區(qū)分LHS和RHS查詢的意義就體現(xiàn)出來了。
如果RHS查詢在所有嵌套的作用域中都沒有找到所需變量,引擎就會拋出 ReferenceError。
如果LHS查詢在所有嵌套的作用域中都沒有找到所需變量,引擎就會在全局作用域中創(chuàng)建一個具有該名稱的變量,并將其返回給引擎。
注意:ES5中引入了嚴格模式,與普通模式相比,嚴格模式其中一個不同就是進制自動或隱式的創(chuàng)建全局變量。因此在嚴格模式下LHS查詢失敗時不會創(chuàng)建并返回全局變量,引擎同樣會拋出 ReferenceError。
感謝各位的閱讀,以上就是“JavaScript作用域的作用是什么”的內(nèi)容了,經(jīng)過本文的學(xué)習(xí)后,相信大家對JavaScript作用域的作用是什么這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關(guān)知識點的文章,歡迎關(guān)注!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。