Day 6:湯姆克魯斯與唐家霸王槍——變數的作用域(Scope) (2)

星爺強碰阿湯哥誰會贏?Global Scope vs. Function Scope

(Source: 網路1 / 網路2)
俗話說天高皇帝遠,十里外的瀑布不如眼前的一杯水。
全球來說,在杜拜第一高樓跳來跳去的阿湯哥毫無疑問有較廣泛的知名度。但對我們而言,星爺在我們的生活中有更深的影響力。
Global 變數和 Local 變數也是如此。
也許對整個程式來說,Local 變數不及 Global 變數效用來得廣,但在函數的 Local 範圍內,Local 變數的存在感比較高
所以如果 Global 和 Function 內宣告了同樣名稱的變數,在 Function 內會是 Local 變數生效,Function 外依然是 Global 稱王。
如下面這個例子:
function myFunc(){
var n1 = "Stephen Chow";
console.log("myFunc(): n1=", n1);
console.log("myFunc(): this.n1=", this.n1);
console.log("myFunc(): window.n1=", window.n1);
}
var n1 = "Tom Cruise";
myFunc();
console.log("Global: n1=", n1);
執行結果:
myFunc(): n1= Stephen Chow
myFunc(): this.n1= Tom Cruise
myFunc(): window.n1= Tom Cruise
Global: n1= Tom Cruise
要注意的是,如果在 Function 內透過如 window.n1 的方式,明確表明「我要取的是 Global 變數的 n1,而不是 Local 的 n1」,一樣會是 Global 變數發揮效用。

Block Level Scope

除了 Global Level 和 Function Level,還有第三種等級:Block Level。
(Source: 網路)
Block Level 就像住在你家隔壁號稱歌神的里長阿伯。
不要小看他們,也許不要說離開這個縣市,可能出了這一里就沒人認得他們,但在這一里的範圍內,他們就是婆婆媽媽們心中最強的情歌王子。
Block Level 的作用域範圍可能非常小,只是一個函數裡的某一段程式。
程式裡 Block 指的是一段用大括號 ({}) 包起來的區塊。
我們用 Java 來舉例:
(為什麼用 Java 舉例而不是用 JavaScript,你馬上就會知道)

Block Scope 的 Java 例子

以下是一段 Block 的例子:
public static void main(String []args){
{
int x = 10;
System.out.println(x);
}
}
執行結果:
10
很正常印出 x 的值,好像沒什麼異狀。
我們改在 Block 外面去呼叫 x 變數:
public static void main(String []args){
{
int x = 10;
}
System.out.println(x);
}
執行結果:
HelloWorld.java:7: error: cannot find symbol
System.out.println(x);
^
symbol: variable x
location: class HelloWorld
1 error
Java 編譯器說他不認得 x 這個變數。
這就是 Block Scope 的效果。雖然在同一個 Function 內,只要一出 Block,變數就失去效用。

JavaScript 的 var 在 Block Scope 使用的例子

輪到 JavaScript 上場了!
我們一樣在 Block 內宣告變數,預期出了 Block 就不認得 n1 變數:
{
var n1 = "OneJar";
}
console.log("Global: n1=", n1);
console.log("Global: this.n1=", this.n1);
console.log("Global: window.n1=", window.n1);
執行結果:
Global: n1= OneJar
Global: this.n1= OneJar
Global: window.n1= OneJar
(Source: 網路)
怎麼還是印得出來?而且是個全域變數。
一定有什麼誤會,可能只有 Function 內的 Block 有效,我們改在 Function 內的小 Block 去宣告:
function myFunc(){
{
var n1 = "OneJar";
}
console.log("myFunc(): n1=", n1);
console.log("myFunc(): this.n1=", this.n1);
console.log("myFunc(): window.n1=", window.n1);
}
myFunc();
執行結果:
myFunc(): n1= OneJar
myFunc(): this.n1= undefined
myFunc(): window.n1= undefined
(Source: 網路)
還是印得出來,雖然不再是 Global 變數,但看起來是 Function Level Scope,而不是上面所描述的 Block Scope。
到底怎麼回事?

對,傳統 JavaScript 只有 Global 和 Function 兩種等級的作用域

(Source: Youtube)
剛剛一大段 Block Scope 都在講辛酸的嗎?
放心,都找了舞棍阿伯出來站台,怎麼可能是假的。
確實在 ES5 以前,用 var 關鍵字去宣告的變數,只會有 Global Level 和 Function Level 兩種等級的作用域
ES6 導入了新的變數宣告關鍵字:letconst,不僅提高變數控管的嚴謹性,也增加了 Block Scope 的用途
明天後面的文章我們就來介紹 ES6 的 letconst

References