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

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

(Source: 網路1 / 網路2)
俗話說天高皇帝遠,十里外的瀑布不如眼前的一杯水。
全球來說,在杜拜第一高樓跳來跳去的阿湯哥毫無疑問有較廣泛的知名度。但對我們而言,星爺在我們的生活中有更深的影響力。
Global 變數和 Local 變數也是如此。
也許對整個程式來說,Local 變數不及 Global 變數效用來得廣,但在函數的 Local 範圍內,Local 變數的存在感比較高
所以如果 Global 和 Function 內宣告了同樣名稱的變數,在 Function 內會是 Local 變數生效,Function 外依然是 Global 稱王。
如下面這個例子:
1
function myFunc(){
2
var n1 = "Stephen Chow";
3
console.log("myFunc(): n1=", n1);
4
console.log("myFunc(): this.n1=", this.n1);
5
console.log("myFunc(): window.n1=", window.n1);
6
}
7
8
var n1 = "Tom Cruise";
9
myFunc();
10
console.log("Global: n1=", n1);
Copied!
執行結果:
1
myFunc(): n1= Stephen Chow
2
myFunc(): this.n1= Tom Cruise
3
myFunc(): window.n1= Tom Cruise
4
Global: n1= Tom Cruise
Copied!
要注意的是,如果在 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 的例子:
1
public static void main(String []args){
2
{
3
int x = 10;
4
System.out.println(x);
5
}
6
}
Copied!
執行結果:
1
10
Copied!
很正常印出 x 的值,好像沒什麼異狀。
我們改在 Block 外面去呼叫 x 變數:
1
public static void main(String []args){
2
{
3
int x = 10;
4
}
5
System.out.println(x);
6
}
Copied!
執行結果:
1
HelloWorld.java:7: error: cannot find symbol
2
System.out.println(x);
3
^
4
symbol: variable x
5
location: class HelloWorld
6
1 error
Copied!
Java 編譯器說他不認得 x 這個變數。
這就是 Block Scope 的效果。雖然在同一個 Function 內,只要一出 Block,變數就失去效用。

JavaScript 的 var 在 Block Scope 使用的例子

輪到 JavaScript 上場了!
我們一樣在 Block 內宣告變數,預期出了 Block 就不認得 n1 變數:
1
{
2
var n1 = "OneJar";
3
}
4
console.log("Global: n1=", n1);
5
console.log("Global: this.n1=", this.n1);
6
console.log("Global: window.n1=", window.n1);
Copied!
執行結果:
1
Global: n1= OneJar
2
Global: this.n1= OneJar
3
Global: window.n1= OneJar
Copied!
(Source: 網路)
怎麼還是印得出來?而且是個全域變數。
一定有什麼誤會,可能只有 Function 內的 Block 有效,我們改在 Function 內的小 Block 去宣告:
1
function myFunc(){
2
{
3
var n1 = "OneJar";
4
}
5
console.log("myFunc(): n1=", n1);
6
console.log("myFunc(): this.n1=", this.n1);
7
console.log("myFunc(): window.n1=", window.n1);
8
}
9
10
myFunc();
Copied!
執行結果:
1
myFunc(): n1= OneJar
2
myFunc(): this.n1= undefined
3
myFunc(): window.n1= undefined
Copied!
(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