你不知道JavaScript上卷 筆記(一)

taryn2016發表於2019-01-17

 作用域是什麼

  1. 編譯原理 

 開發者大多把JavaScript歸為“動態”或者“解釋執行”語言,但是實際上JavaScript也是編譯語言,只是並不是提前編譯的。JavaScript的編譯發生在程式碼執行前的幾微秒。 

 程式碼的編譯分為以下步驟: 

 1. 分詞/詞法分析 以JavaScript為例, var a = 2; 這段程式會被分解成以下詞法單元: var、a、=、2、;。空格是否會當做詞法單元取決於空格在這門語言中是否有意義 

 2. 解析/語法分析 將分詞得到的詞法單元轉換成“抽象語法樹”(AST) 

 3. 程式碼生成 將AST轉換為可執行程式碼的過程 然而,比起上述的編譯過程只有三個步驟的語言的編譯器,JavaScript的引擎要複雜得多。JavaScript引擎在語法分析和程式碼生成階段會對執行效能進行優化,包括對冗餘元素的優化。 

2.理解作用域 

 在作用域中參與工作的有以下幾個部分: 引擎、編譯器、作用域。 當執行var a = 2;時 編譯器先將程式碼分解成詞法單元,然後解析為“抽象語法樹”。當開始進行程式碼生成時,對這段程式碼的處理方式會和預期的有所不同。

 編譯器的處理如下: 

 1. 遇到var a,會先去詢問該作用域是否已經存在a變數。若存在,則忽略var 宣告;否則要求作用域在該作用域生成一個變數a。 

 2. 接下來編譯器生成引擎執行時所需的程式碼,這些程式碼唄用來處理a = 2這個賦值操作。引擎執行時先詢問作用域當前作用域是否存在一個叫a的變數,若是,則使用這個變數,否則繼續查詢該變數直到找到,或者到全域性作用域(不管找沒找到)而停止。 

 總結:變數賦值的的過程概括來說就是 1. 編譯器宣告變數。2. 引擎查詢變數,找到賦值。 

 3.LHS和RHS 

區分LHS和RHS是一件非常重要的事情!!!! 

1. LHS:查詢變數賦值操作的目標 

2. RHS:取源值,非LHS即不是賦值操作的目標都為RHS

以下為例子: 

你不知道JavaScript上卷 筆記(一)

引擎和作用域: 引擎想作用域去討要引用,引擎執行程式碼操作 

4.作用域巢狀 

在當前作用域無法找到某個變數時,引擎會在外層作用域繼續查詢,知道找到該變數,或者到全域性作用域(不管找沒找到)而停止。  

5.異常 

現在我們來講講為什麼區分LHS和RHS是一件重要的事情。 

在變數還沒有宣告(在任何作用域中都無法找到該變數)的情況下,這兩種查詢行為不同。 

下列程式碼的真實操作為右邊的程式碼塊

                                var b
function foo(a) {               function foo(a) {
    console.log(a + b)   ==>        console.log(a + b) // 這裡對b是RHS查詢的,因為無法找到,所以丟擲ReferenceError錯誤
    b = a                           b = a  //這裡對b進行LHS查詢,直到全域性作用域都沒有查到,又因不是strict模式,則在全域性作用域自動建立了b
}                                }
foo(2)複製程式碼

RHS查詢: 在所有巢狀的作用域中都無法找到時,會丟擲ReferenceError

LHS查詢:無法找到時分為兩種情況 

 1. 非strict模式下: 會熱心的建立一個變數,當然是在全域性作用域中建立。 

 2. 在strict模式下: 同樣會丟擲ReferenceError 

另外RHS找到了這個變數,

但是你對這個變數進行不合理的操作,如:非函式型別值的函式呼叫、引用null/undefined型別的值的屬性。則會丟擲TypeError。 ReferenceError錯誤和作用域判別失敗相關,而TypeError則是作用域判別成功,但是對結果操作是非法或不合理的。 

注: LSH和RSH都是從當前作用域開始向頂層查詢的。 


相關文章