前幾天的文章談到各種等級的 Scope 效果。
懶人包支援:
在 JavaScript 裡,有 3 種等級的作用域:
- 香港喜劇天王星爺——Function Level Scope
- 國際巨星阿湯哥——Global Level Scope
- 住在隔壁號稱歌神的里長阿伯——Block Level Scope (ES6)
不同情境下各種 Scope 如何作用,有時光靠文字描述仍略覺模糊。
尤其同樣在主程式宣告,使用 let
和 const
產生的變數是 Block Scope,使用 var
產生的變數是 Global Scope 這一段,相對不易理解。
俗話說一張圖勝過千言萬語,這篇文章的目標是將不同 Scope 的情境用圖解的方式說明,希望讓 Scope 一目瞭然。
上面這張圖代表我們一般撰寫 JavaScript 程式的程式結構。
我們會在主程式區——正式一點的名稱為全域執行環境 (Global Execution Context) ——開始撰寫程式 (意即程式碼不在任何函數內)。
過程中會用到如 if-else
或 for loop
,形成一個個 Block (如 Block A
、Block B
)。
漸漸地,我們會把一些程式片段包裝成函數,形成 Function 區塊 (如 myFunc1()
、myFunc2()
),而函數內又可能再形成小 Block。
對應到實際的程式碼,會類似以下:
/* Global Execution Context (outside of any function) */
var i;
console.log(i);
// Block A
if(true){
.......
}
// Block B
for( i = 0 ; i < 5 ; i ++){
.......
}
function myFunc1(){
..........
// Block C
if(true){
........
}
// Block D
if(true){
.........
}
}
function myFunc2(){
..........
// Block E
if(true){
........
}
// Block F
if(true){
.........
}
}
以下會用實際的程式碼情境,配合程式結構圖片來說明各種 Scope 的有效範圍。
var
、let
、const
都一樣)function myFunc(){
var n1 = "OneJar";
console.log("myFunc(): typeof n1=", typeof n1, " value=", n1);
}
myFunc();
console.log("Global: typeof n1=", typeof n1); // 這裡 n1 只能印 type 不能印值,否則會拋 `ReferenceError: n1 is not defined`
執行結果:
myFunc(): n1= OneJar
Global: typeof n1= undefined
紅色是 n1
宣告的地方,淺藍色部分就是 n1
的 Scope。
var
)function myFunc(){
console.log("myFunc(): n1=", n1);
console.log("myFunc(): this.n1=", this.n1);
console.log("myFunc(): window.n1=", window.n1);
}
var n1 = "OneJar";
myFunc();
console.log("Global: n1=", n1);
執行結果:
myFunc(): n1= OneJar
myFunc(): this.n1= OneJar
myFunc(): window.n1= OneJar
Global: n1= OneJar
n1
使用 var
宣告在主程式區,會存放在 Global Object 裡,屬於 Global 變數。let
或 const
)function myFunc(){
console.log("myFunc(): n1=", n1);
console.log("myFunc(): this.n1=", this.n1);
console.log("myFunc(): window.n1=", window.n1);
}
let n1 = "OneJar";
myFunc();
console.log("Main: n1=", n1);
執行結果:
myFunc(): n1= OneJar
myFunc(): this.n1= undefined
myFunc(): window.n1= undefined
Main: n1= OneJar
Main()
)。n1
不會成為 Global Scope,而是 Function Scope / Block Scope。function myFunc(){
n1 = "OneJar"; // 自動變成一個 Global 變數
console.log("myFunc(): n1=", n1);
console.log("myFunc(): this.n1=", this.n1);
console.log("myFunc(): window.n1=", window.n1);
}
myFunc();
console.log("Global: n1=", n1);
執行結果:
myFunc(): n1= OneJar
myFunc(): this.n1= OneJar
myFunc(): window.n1= OneJar
Global: n1= OneJar
n1 = "OneJar"
,也就是沒有宣告就對 n1
賦值的地方。n1
產生為 Global 變數,所以變成 Global Scope。var
、let
、const
都一樣)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
var n1 = "Tom Cruise"
,宣告在主程式區,屬於 Global Scope。var n1 = "Stephen Chow"
,宣告在主程式區,屬於 Function Scope。n1
。n1
。var
宣告if(true){
var x = 2;
{
console.log(x); // 2
}
console.log(x); // 2
}
console.log(x); // 2
var
不支援 Block Scope。Block C
內),會是 Function Scope。let
或 const
宣告if(true){
let x = 2;
{
console.log(x); // 2
}
console.log(x); // 2
}
console.log(x); // ReferenceError: x is not defined
let
或 const
宣告變數,支援 Block Scope 效果。x
只會在被宣告的那個 Block 和其子 Block 被認得。作用域 (Scope) 在程式設計裡是滿重要的一個概念。
不同程式語言隨著不同特性,可能有不同的 Scope 類型。
例如 Java 是物件導向語言,在物件的成員變數和函式前面會加上 public
或 private
等修飾子來宣告作用域,和 JavaScript 的作用域又是不同的運作原理。
不管是使用哪一種語言開發,都應該注意該語言的作用域運作原理。