iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 14
1
Modern Web

【這些年我似是非懂的 Javascript】系列 第 14

【這些年我似是非懂的 Javascript】Day 14 - 範疇

什麼是範疇?
你有想過當你將值存進變數時,那變數放哪嗎?
而所謂的範疇就是一組規則用來定義變數儲存的位置。

接著我們正式進入第二本書的內容囉!

編譯器在幹嘛?

沒聽過編譯器也看過編譯器走路 (?
網路上搜尋 JS 大概幾乎直接都是看到說 JS 是一個直譯式的語言,但是你知道...他其實是編譯式語言嗎?

JS 特別的是他並不是像許多傳統的編譯式語言一樣,預先就編譯好,而是在快要執行的時候才編譯。
(聽起來有點臨時抱佛腳的感覺 xD)

傳統編譯式語言到底做了哪些事?
通常在執行前會經歷的是三個步驟,而這三個步驟大致上就稱為編譯。
以下粗略分享一下。

  • Tokennizing(語法基本單元化) and Lexing(語彙分析)
    將一串字元拆分成有意義的組塊(chunks),譬如說var a = 17;,他就可能會拆成 vara=2; 之類的。

    空白有可能會有可能不會,會依照情境決定他是否有意義才決定。

  • Parsing(剖析)
    基本上就是將上面拆出來的變成一個 AST(抽象與法樹)。

  • Code-Generation
    接續上面的將拆出來的 AST 轉成可執行的一組機器指令,並實際建立出變數等等的再將值丟進變數。

而 JS 做得比上面說的三個單純的步驟來得複雜,在剖析過程中會有最佳化裡面可能包含了消除多餘元素(例如多餘的空白之類的)。

跟範疇交朋友

以範例來說

var a = 17;

這個述句在處理的方式會是如何?
主要有三個重要的角色

  1. Engine (引擎)
  2. Compiler (編譯器)
  3. Scope (範疇)

那我們就來看一下他們對於上面的述句負責的事~

  1. Compiler 遇到 var a ,他就會問 Scope,這個 a ,是否有在這特定的範疇集合內? 有的話就忽略宣告的動作繼續前進,沒有的話他就會要求 Scope 為那個範疇集合宣告 a 這個變數。
  2. Compiler 產生 Engine 看得懂的程式碼後會讓 Engine 去處理 a = 2 的指定式, Engine 就會跑去問 Scope 說目前範疇有沒有一個 a 的變數可以用? 如果有就拿來,沒有就會往外面的地方找,找到就恭喜老爺賀喜夫人,他就會把 2 塞給他,如果找不到的話 Engine 就會噴你錯誤。

LHS 和 RHS

剛剛上面講到第二步時 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);

...

...

一起來找看看 LHSRHS 有多少個吧!

...

...

好了嗎?
公佈答案囉~

...

這邊的 LHS 查找就有 3 個!

  1. c = ..
  2. b = 2 (隱含的參數在 foo旁邊 xD)
  3. a = ..

RHS 查找則有 4 個!

  1. foo(2..
  2. = a;
  3. a..
  4. b..

不是啊!
所以我說幹嘛要區分 LHSRHS
我看不出來重點在哪?我哪裡需要?
主要是為了知道你在寫程式時,知道 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))


上一篇
【這些年我似是非懂的 Javascript】Day 13 - 文法 # 趴兔
下一篇
【這些年我似是非懂的 Javascript】Day 15 - 語彙範疇
系列文
【這些年我似是非懂的 Javascript】34

尚未有邦友留言

立即登入留言