Lexical Scope
講了那麼多,其實就是會 nested 的 scope , 通常在 compiler 就 parser 完成。
所以想到 Lexical Scope ,就要想到第一階段的 compiler time
。我們小球和木桶的對話。
dynamic scope
常見聽到的是 Bash
,也是最多人誤解 JavaScript 的環節。
像是 Bash script,並沒有 compiler 的階段,所以無法產生 Lexical Scope,就只是一行執行一行,也就是 rum time
執行。
很像的這張圖,現在應該很清楚明白,我們是 compiler time
先決定好 Scope 了,所以不用等到rum time,我們知道 teacher 是藍色小球的 Scope。
希望看到 Lexical Scope ,大家就想到 compiler time
,也想到我們的這張圖:
如果你用 sublime,你可以找這個套件 (ES levels)
https://packagecontrol.io/packages/Levels
會有點像這樣:
line 7 的 teacher 會是和上層 function 一樣顏色。
*註:這個套件對 named function expression 有小小 bug, 都會是 global ,
因為要處理這個部分,會產生太多顏色,避免專案太過麻煩,所以這個 bug 沒有修。
JavaScript
不是
Dynamic scope。
如果 JavaScript 是 Dynamic scope ,你會看到以下錯誤:
執行到 ask() , 但不知道 teacher 在哪邊,或是說, ask() 放在每個不同的位置,每次都會產生不同結果,因為 runtime 才決定要顯示什麼值。
var teacher = "Kyle";
// do somethings
console.log(teacher); // Kyle
以上code 沒有太多問題,唯一的問題,可能是 do somethings
比如有人在半年後改成這樣:
var teacher = "Kyle";
// 6 months now.
// 我有新的需求,來改點 code ...
var teacher = "Suzy";
console.log(teacher); // Suzy
// ...
// end
console.log(teacher); // Suzy -- OOPS!!
你的 code 爆炸了,可能是有人沒看到,沒注意變數被用過,反正你的程式壞了。
你可能覺得 「那我用 const 啊!」
那我還是可以把所有 const 改成 var ...
真正問題不是 變數被重新賦值
,
問題是 變數命名重複問題 (naming collision problem)
。
最好的解決方法,就是把不同變數
放在不同桶子(scope)
也就是我們一直出現的範例:
這邊的
var teacher
就不會被改寫了,大家的區域分明。
不過還是一樣都是var teacher
,沒有完全解決變數命名重複問題 (naming collision problem)
如果你讀資工或相關書籍,會有讀過 最小權限原則(Principle of least privilege)
,
你應該預設把值隱藏起來(keeping everying private),只把最少量真正需要的訊息公開。
如果你按照最小權限原則
,你可以處理掉三個問題:
重構(refactoring)
, code 不會因為被其他人使用而不能改動。如何做到? 看看 line 8
把一個變數,當成 expression 。
如圖,取代掉原本的變數。
Q: 為什麼不是 function declaration?
A: 因為不是 function
當做第一個字開頭
。
這樣 JavaScript 才可以判斷是 function expression(IIFE) 還是 function declaration
順帶一提,你常看到的模式應該長這樣,
但是 Kyle Simpson 史上超級無敵討厭 匿名函式(Anonymous function),
所以他的範例都會給名字,就算沒有什麼特別的命名,他至少還是會給IIFE
這個字當作 fn 名字 XDD
看到
IIFE
總比看到 anonymous function 心情還要好。
當然,IIFE 也是可以傳參數:
IIFE 特性是把 statement 轉換成 function expression 來執行。
如果你要用 error handler 處理 assignment,並不會被捕捉到,
所以轉成 IIFE 用 expression 來執行,就可以被捕捉到了。
範例是先宣告在 line 1, 後面 line 3, line 6 就是 expression
如果你用 IIFE ,可以不用寫多餘的 teacher
變數。
這個例子不常見,只是說明你可以善用 IIFE 的特性。
{..}
比較 IIFE 和 block 的寫法:
這邊你應該注意到,不能用 var
。
也是寫到快結束,才真正開始出現 let
。
{..}
) 一起出現: block 內部轉換(implicit)成有 scopelet 這邊改成 const 一樣效果。
{..}
) 一起出現: block 內部不會
有 scope你原本寫可能是這樣:
但是如果你想強調
Scope , 使用 let :
Kyle Simpson 不使用 let, const
替換所有 var
,因為他的 let, const有語意(block scope)
。
氣死,超好笑
比如,
for loop
裡面 應該使用 let
Kyle :「你應該學會你的工具,然後好好使用。」
順帶一提,你用 ESlint 會提示不要用 var , Kyle 關掉那個提示了 XDDD
let + {..}
==> Scope 。*註:這邊的 code 也只是舉例,不是真實世界常見的 code。
但是 Kyle 如果在寫長 funtion ,他總是重複宣告 var
var
關鍵字有宣告的語意,天生可以重複使用。使用 let 的建議: 你應該總是記得有 block
{..}
否則,你使用的 let 可能沒有語意。
Kyle 會故意把 let 的宣告和 {
放在同一行,來表示 let block scope。
line 8 : 我們沒有 reassign teachers
,我們的對象是 teachers[1]
*註:Java 把 const keyword 改成 final
,希望減少誤解 。
這邊 Kyle 舉一個很有趣的例子:const teachers = ["Kyle", "Suzy"];
我們現在都知道,其實有各種方法可以改掉 teachers 這個 array。
但是慣例上
大家還是用 const 。
就像是小孩子害怕衣櫃裡面有怪物,所以我們幫小朋友留一盞夜燈。
但是,我們都知道,衣櫃裡面不可能有怪物
。
Kyle 自己的意見:
let
在需要 block scope 的時候。比如數學的常數 , 比如 API 的 base URL
註:明天終於來到 hoisting !!