深度剖析JavaScript作用域從局部到全局一網(wǎng)打盡
目錄
- JavaScript作用域深度剖析:從局部到全局一網(wǎng)打盡
- 1.1 編譯原理
- 1.2 理解作用域
- 1.2.1 演員表
- 1.2.2 對話
- 1.2.3 編譯器有話說
- 1.2.5
- 1.3 作用域嵌套
- 1.4 異常
- 1.5 小結(jié)
- 特殊字符描述:
JavaScript作用域深度剖析:從局部到全局一網(wǎng)打盡
1.1 編譯原理
JavaScript
事實上是一門編譯語言。
在傳統(tǒng)編譯語言中,一段源代碼執(zhí)行前會經(jīng)歷三個步驟:
分詞/詞法分析(Tokenizing/Lexing)
var a = 2;// 分解后:var、a、=、2、;// 空格是否會被當做詞法單元,取決于空格在這門語言中是否具有意義。
- 期間經(jīng)過兩個過程:分詞(tokenizing)和詞法分析(Lexing) 、兩者的主要差別在于詞法單元的識別是通過
有狀態(tài)
還是無狀態(tài)
的方式進行的。
解析/語法分析(Parsing)
- 這個過程就是將詞法單元流(數(shù)組)轉(zhuǎn)換為一個由元素逐級嵌套組成的代表了程序語法結(jié)構(gòu)的樹,這個樹被稱為"抽象語法樹"。(Abstract Syntax Tree, AST)。
代碼生成
- 將 AST 轉(zhuǎn)換為可執(zhí)行代碼的過程被稱為代碼生成。也就是說有某種方法將 var a = 2; 的 AST 轉(zhuǎn)換為一組機器指令,用來創(chuàng)建一個叫做 a 的變量(包含分配內(nèi)存等),將一個值儲存于 a 中。
- 比起其他編譯過程只有這三個步驟的語言的編譯器,JavaScript 引擎要復雜得多,在語法分析和代碼生成階段有著特定的步驟來對比運行性能進行優(yōu)化,包括對冗余元素進行優(yōu)化等。
- 簡單來說,任何 JavaScript 代碼片段在執(zhí)行前都要進行編譯(通常就在執(zhí)行前)
1.2 理解作用域
1.2.1 演員表
- 引擎:從頭到尾負責整個 JavaScript 程序的編譯及執(zhí)行過程。
- 編譯器:引擎的好朋友之一,負責語法分析及代碼生成等臟活累活。
作用域
:引擎的另一個好朋友,負責收集并維護由所有聲明的標識符(變量)組成的一系列查詢,并實行一套嚴格的規(guī)則,確定當前執(zhí)行的代碼對這些標識符的訪問權(quán)限。
1.2.2 對話
var a = 2;
這段代碼是一句聲明。但會經(jīng)過編譯器和引擎的處理來進行。- S: 變量的賦值操作會執(zhí)行兩個動作,首先編譯器會在當前作用域中聲明一個變量(如果之前沒有聲明過),然后在運行時引擎會在作用域中查找該便令,如果能夠找到就會對它進行賦值。
1.2.3 編譯器有話說
編譯器在編譯過程中的第二步中生成了代碼,引擎執(zhí)行它時,會通過查找變量 a 來判斷他是否已聲明過。查找的過程由作用域進行協(xié)助,但是引擎執(zhí)行怎樣的查找會影響最終的查找結(jié)果。
引擎常使用的查詢類型為:LHS和RHS
LHS: 賦值操作的目標是誰
RHS: 誰是賦值操作的源頭
1.2.5
function foo(a) { var b = a; return a + b;}var c = foo(2);// 對話:1. 聲明 var c2. 對 c 進行 LHS3. 對 foo(2) 進行 RHS4. function foo(a) 期間會進行 a = 2, 對 a 進行 LHS5. 聲明 var b6. 對 b 進行 LHS7. 對 a 進行 RHS8. return a + b; 分別對 a、b 進行 RHS// 答案:1. 所有的 LHS(一共有3處) 1. c =..; 2. a = 2(隱士變量分配) 3. b = ..2. 所有的 RHS (一共有4處) 1. foo(2.. 2. = a; 3. a.. 4. .. b
1.3 作用域嵌套
作用域是根據(jù)名稱查找變量的一套規(guī)則。
當一個塊或函數(shù)嵌套在另一個塊或函數(shù)中時,就會發(fā)生作用域的嵌套。因此在當前作用域中無法找到某個變量時,引擎就會在外層作用域中繼續(xù)查找,直到找到該變量,或抵達最外層的作用域(也就是全局作用域)為止。
// 非嚴格模式下function foo(a) {console.log(a + b);}var a = 2;foo(2); // 4// 嚴格模式下:function foo(a) {console.log(a + b);}var a = 2;foo(2); // 4
遍歷嵌套作用域鏈的規(guī)則:引擎會從當前的執(zhí)行作用域中開始查找變量,如果找不到就會向上一級中繼續(xù)查找。當?shù)诌_最外層的全局作用域時,無論找到還是沒找到,查找的過程都會停止。
例子:
- 整個建筑代表程序中的嵌套作用域鏈,第一層樓代表當前的執(zhí)行作用域,也就是你所處的位置。建筑的頂層代表全局作用域。
- 引擎查找的方式:LHS 和 RHS 引用會先在當前樓層中進行查找,如果沒找到,就會坐電梯前往上一層樓樓,如果還是沒找到就會繼續(xù)上下,以此類推。一旦達到了頂層(全局作用域), 可能找到你了你所需的變量,也可能沒找到,但無論如何查找過程都會停止。
1.4 異常
為什么區(qū)分 LHS 與 RHS 是一種重要的事?
因為在變量還未聲明(在任何作用域中都無法找到該變量)的情況下,引擎的這兩種查詢行為是不一樣的。
// 非嚴格模式下:function foo(a) {console.log(a + b);b = a;}foo(2); // 4// 嚴格模式下:"use strict";function foo(a) {console.log(a + b);b = a;}foo(2); // ReferenceError: b is not defined
上述代碼引擎行為:
- 非嚴格模式下:
第一次對 b(.. + b) 進行 RHS 查詢時未找到該變量,也就是說,這是一個"未聲明" 的變量,因為在任何相關(guān)的作用域都無法找到它。
第二次對 b(b = ..) 進行 LHS 查詢時,如果在頂層(全局作用域)中也沒找到該變量,就會在全局作用域中隱式地創(chuàng)建一個該名稱的變量,并將其返回給引擎。
......
- 嚴格模式下:
第一次對 b(.. + b) 進行 RHS 查詢時未找到該變量,也就是說,這是一個"未聲明" 的變量,因為在任何相關(guān)的作用域都無法找到它,直接拋出 'ReferenceError
'。
......
非嚴格模式下引擎查找規(guī)則:
當引擎執(zhí)行 RHS 查詢在所有嵌套的作用域中找不到所需的變量,引擎就會拋出
ReferenceError
異常。當引擎執(zhí)行 LHS 查詢時,如果在頂層作用域中也無法找到該變量,全局作用域就會創(chuàng)建一個該名稱的變量,并將其返回給引擎(非嚴格模式下)。
嚴格模式下引擎查找規(guī)則:
ES5 引入了
"嚴格模式"(use strict)
,在行為上有很多不同,其中一個不同的行為就是嚴格模式下禁止自動或隱式地創(chuàng)建全局變量。因此在嚴格模式中引擎執(zhí)行 LHS 查詢失敗時,并不會創(chuàng)建一個全局變量,而是直接拋出一個ReferenceError
。如果 RHS 找到了一個變量,但嘗試對這個變量進行一些不合理的操作時,比如對一個非函數(shù)類型的值進行函數(shù)調(diào)用,或者引用
null
或undefined
類型的之中屬性,那引擎則會拋出另外一種類型的異常TypeError。
ReferenceError
同作用域判斷失敗相關(guān),而TypeError
代表作用域判別成功了,但對結(jié)果的操作是非法或不合理的。
1.5 小結(jié)
作用域是根據(jù)名稱查找變量的一套規(guī)則。
引擎常使用的查詢類型為:LHS 和 RHS
- LHS: 賦值操作的目標是誰
= 操作符
在調(diào)用函數(shù)時的形參會導致關(guān)聯(lián)作用的賦值操作。也就是說 foo (a, b, c...), 都會有 a = xxx, b = xxx, c = xxx ...... 的行為。- RHS: 誰是賦值操作的源頭
非嚴格模式下引擎查找規(guī)則:
當引擎執(zhí)行 RHS 查詢在所有嵌套的作用域中找不到所需的變量,引擎就會拋出 ReferenceError
異常。
當引擎執(zhí)行 LHS 查詢時,如果在頂層作用域中也無法找到該變量,全局作用域就會創(chuàng)建一個該名稱的變量,并將其返回給引擎(非嚴格模式下)。
嚴格模式下引擎查找規(guī)則:
ES5 引入了 "嚴格模式"(use strict)
,在行為上有很多不同,其中一個不同的行為就是嚴格模式下禁止自動或隱式地創(chuàng)建全局變量。因此在嚴格模式中引擎執(zhí)行 LHS 查詢失敗時,并不會創(chuàng)建一個全局變量,而是直接拋出一個 ReferenceError
。
如果 RHS 找到了一個變量,但嘗試對這個變量進行一些不合理的操作時,比如對一個非函數(shù)類型的值進行函數(shù)調(diào)用,或者引用 null
或 undefined
類型的之中屬性,那引擎則會拋出另外一種類型的異常 TypeError。
ReferenceError
同作用域判斷失敗相關(guān),而TypeError
代表作用域判別成功了,但對結(jié)果的操作是非法或不合理的。
特殊字符描述:
- 問題標注
Q:(question)
- 答案標注
R:(result)
- 注意事項標準:
A:(attention matters)
- 詳情描述標注:
D:(detail info)
- 總結(jié)標注:
S:(summary)
- 分析標注:
Ana:(analysis)
- 提示標注:
T:(tips)
以上就是深度剖析JavaScript作用域從局部到全局一網(wǎng)打盡的詳細內(nèi)容,更多關(guān)于JavaScript作用域局部全局的資料請關(guān)注其它相關(guān)文章!
相關(guān)文章:
