
什麼是範疇?
你有想過當你將值存進變數時,那變數放哪嗎?
而所謂的範疇就是一組規則用來定義變數儲存的位置。
接著我們正式進入第二本書的內容囉!
沒聽過編譯器也看過編譯器走路 (?
網路上搜尋 JS 大概幾乎直接都是看到說 JS 是一個直譯式的語言,但是你知道...他其實是編譯式語言嗎?

JS 特別的是他並不是像許多傳統的編譯式語言一樣,預先就編譯好,而是在快要執行的時候才編譯。
(聽起來有點臨時抱佛腳的感覺 xD)
傳統編譯式語言到底做了哪些事?
通常在執行前會經歷的是三個步驟,而這三個步驟大致上就稱為編譯。
以下粗略分享一下。
Tokennizing(語法基本單元化) and Lexing(語彙分析)
將一串字元拆分成有意義的組塊(chunks),譬如說var a = 17;,他就可能會拆成 var、a、=、2 和 ; 之類的。
空白有可能會有可能不會,會依照情境決定他是否有意義才決定。
Parsing(剖析)
基本上就是將上面拆出來的變成一個 AST(抽象與法樹)。
Code-Generation
接續上面的將拆出來的 AST 轉成可執行的一組機器指令,並實際建立出變數等等的再將值丟進變數。
而 JS 做得比上面說的三個單純的步驟來得複雜,在剖析過程中會有最佳化裡面可能包含了消除多餘元素(例如多餘的空白之類的)。

以範例來說
var a = 17;
這個述句在處理的方式會是如何?
主要有三個重要的角色
那我們就來看一下他們對於上面的述句負責的事~
Compiler 遇到 var a ,他就會問 Scope,這個 a ,是否有在這特定的範疇集合內? 有的話就忽略宣告的動作繼續前進,沒有的話他就會要求 Scope 為那個範疇集合宣告 a 這個變數。Compiler 產生 Engine 看得懂的程式碼後會讓 Engine 去處理 a = 2 的指定式, Engine 就會跑去問 Scope 說目前範疇有沒有一個 a 的變數可以用? 如果有就拿來,沒有就會往外面的地方找,找到就恭喜老爺賀喜夫人,他就會把 2 塞給他,如果找不到的話 Engine 就會噴你錯誤。
剛剛上面講到第二步時 Engine 去查找 a 是否被宣告,而這個問 Scope 的動作種類有分 LHS (Lefthand side) 和 RHS (Righthand side),這個查找的方向不同結果自然也就不同。
當一個變數出現在一個指定作業的左手邊,進行的就是 LHS ,反之則是 RHS (但 RHS 不是說指定的在右手邊,而是說他不是左手邊)。
舉例來說
function foo(b){
var a = b;
return a + b;
}
var c = foo(2);
...
...
一起來找看看 LHS 和 RHS 有多少個吧!
...
...
好了嗎?
公佈答案囉~
...
這邊的 LHS 查找就有 3 個!
RHS 查找則有 4 個!

不是啊!
所以我說幹嘛要區分 LHS 和 RHS,
我看不出來重點在哪?我哪裡需要?
主要是為了知道你在寫程式時,知道 JS 報的錯是為了什麼。
譬如說以下範例
function foo(a){
console.log(a+b);
b = a;
}
foo(2);
當 RHS 查找 b 這第一次出現的時候,他找不到所以稱之為為宣告的變數(undeclared),最終如果他在巢狀迴圈找不到,他就會拋出一個 ReferenceError。
但是!!!
對於 LHS 而言的行為就很不一樣,
之前有介紹過嚴格模式對吧!? (沒有的話可以回去看~LHS 的行為模式會依照有沒有開啟嚴格模式而改變
ReferenceError

剛剛有提到當 Engine 找不到該變數在指定的範疇集合時,他就會往外找直到找到最外層全域變數為止。
例如下面的範例
function foo(a){
return a + b;
}
var b = 3;
foo(3);
foo 這 function 裡頭並沒有 b 這個變數,於是 Engine 就往外找到 b 這變數。
以上
感謝各位讀者讀到這~
明天繼續
最近公司各種誘惑啊~又是烤肉又是火鍋的 Orz
我會堅持住的 (吧 QQ
你所不知道的 JS|範疇與 Closures,this 與物件原型 (You Don't Know JS: this & Object Prototypes))