墨言妹帶你細讀《你不知道的
JavaScript
》系列的世界,深刻JavaScript
語言內部,弄清楚JavaScript
每個零部件的用途,知其然更要知其因此然。git
一般,把 JavaScript
歸類爲 「 動態 」 或 「 解釋執行 」 的語言,可是事實上它是一門 編譯語言,不提早編譯,編譯結果也不在分佈式系統中進行移植。github
JavaScript 引擎進行編譯的步驟和傳統的編譯語言很是類似,在某些環節比它要複雜。 編程
傳統編譯語言,在執行以前的三個步驟,統稱爲 「 編譯 」 。數組
分詞/詞法分析( Tokenizing/Lexing
)bash
將有字符組成的字符串分解成(對編程語言來講)有意義的代碼塊,這些代碼塊被稱爲詞法單元( token
)。編程語言
var a = 2;
複製代碼
被分解成詞法單元:var
、a
、 =
、2
、;
。空格在該語言中有意義,則會被當作詞法單元,不然不是。分佈式
解析/語法分析( Parsing
)函數
將詞法單元流(數組)轉換成一個由元素逐級嵌套所組成的表明了程序語法結構的 「 抽象語法樹 」( Abstract Syntax Tree ,
AST
)。post
var a = 2;
複製代碼
以上代碼的抽象語法樹以下所示:性能
VariableDeclaration
頂級節點
Identifer
子節點,值爲 a
AssignmentExpression
子節點
NumericLiteral
子節點,值爲 2
代碼生成
將 AST
轉換爲可執代碼的過程被稱爲代碼生成。這個過程與語言、目標平臺等相關。
即經過某種方法,將 var a = 2 ;
的 AST
轉化爲一組機器指令,用來建立一個變量 a
,並將值存儲在 a
中。
引擎,能夠根據須要建立並存儲變量。
JavaScript
程序的編譯及執行過程。JavaScript
引擎是如何處理 JavaScript
代碼的?
好比 var a = 2;
存在2個不一樣的聲明,變量的賦值操做會執行兩個動做。
var a
,在編譯階段,編譯器先詢問當前做用域,在做用域集合中是否存在變量 a
,若不存在則聲明一個新變量名爲 a
;接着編譯器會爲引擎生成運行時所需的代碼,處理 a = 2
這個賦值操做。a = 2
),在執行階段,引擎運行時先詢問做用域,在做用域中查找該變量 a
,若是找到就將值 2
賦值給變量 a
,不然引擎就會舉手示意並拋出一個異常。如何理解引擎、編譯器、做用域的關係
a
來判斷它是否已經聲明過。JavaScript
引擎的性能要求很高。引擎查找的兩種方式: RHS
和 LHS
LHS
查詢(左側):找到變量的容器自己,而後對其賦值,即賦值操做的目標是誰。好比 a = 2;
,爲 = 2
這個賦值操做找到一個目標。RHS
查詢(非左側):查找某個變量的值,理解爲 retrieve his source value
,即誰是賦值操做的源頭。好比: console.log( a );
,須要獲取到變量 a
的值,則對變量 a
的 RHS
查詢,並傳值給 console.log(...)
。function foo(a){
console.log( a ); //2
}
foo(2);
複製代碼
上述代碼共有1處 LHS
查詢,3處 RHS
查詢。
LHS
查詢有:
a = 2
中,在 2
被當作參數傳遞給 foo(...)
函數時,須要對參數 a
進行 LHS
查詢RHS
查詢有:
foo(...)
函數的調用須要對 foo
進行 RHS
查詢,意味着 「去找到 foo
的值,並把它給我 」 ,而且 (...)
意味着 foo
的值須要被執行,所以它最好真的示意函數類型的值。console.log( a );
中對 a
進行 RHS
查詢,而且將獲得的值傳給了 console.log(...)
。console.log(...)
自己對 console
對象進行 RHS
查詢,而且檢查獲得的值中是否有一個叫做 log
的方法。做用域是一套規則,用於肯定在何處以及如何查找變量(標識符)。當一個塊或函數嵌套在另外一個塊或函數中時,就發生了做用域的嵌套。
LHS
查詢;若是目的是獲取變量的值,就會使用 RHS
查詢。LHS
查詢。=
操做符或調用函數時傳入參數的操做都會致使關聯做用域的賦值操做。遍歷嵌套做用域鏈的規則: 引擎從當前的執行做用域開始查找變量,若是找不到,就向上一級繼續查找。當抵達最外層的全局做用域時,不管找到仍是沒找到,查找過程都會中止。
爲何區分 LHS
和 RHS
?變量尚未聲明(在任何做用域中都沒法找到該變量)的狀況下,這兩種查詢的行爲是不同的。
function foo(a){
console.log(a + b);
b = a;
}
foo(2);
複製代碼
對一個 「未聲明 」 的變量 b
進行 RHS
查詢時,在任何相關的做用域中都沒法找到它。
ReferenceError
和做用域判別失敗相關,而 TypeError
則表明做用域判別成功了,可是對結果的操做是非法或不合理的。
RHS
查詢在做用域鏈中搜索不到所需的變量,引擎會拋出 ReferenceError
異常。LHS
查詢在做用域鏈中搜索不到所需的變量,全局做用域中會建立一個具備該名稱的變量並返還給引擎。ES5
開始,禁止自動或隱式地建立全局變量), LHS
查詢失敗並不會建立並返回一個全局變量,引擎會拋出同 RHS
查詢失敗時相似的 ReferenceError
異常。RHS
查詢成功狀況下,對變量進行不合理的操做,引擎會拋出 TypeError
異常。(好比對非函數類型的值進行函數調用,或者引用 null
或 undefined
類型的值中的屬性)。最後, 書讀百遍其義自見,抱着以教爲學的初衷,不斷反思、刻意練習,若對你有幫助,請點個贊,謝謝您的支持與指教。
參考文獻: 木易楊博客
歷史文章: 【譯】30 Seconds of ES6 (一)