作用域(Scope)是JavaScript 的重要基礎觀念,在先前JavaScript 變數宣告有哪些?中有提到各個變數的作用範圍,那麼變數的作用範圍與作用域的關係為何?而作用域的概念對於之後了解閉包(Closures)的功用很有幫助。希望在這篇筆記裡可以幫助你了解下面作用域(Scope)的幾個問題。
- 作用域是什麼?
- JavaScript的作用域有哪些?
- 作用域為何重要?
- 作用域鏈是什麼?
作用域是什麼?
這邊看看MDN的說明:
MDN-Scope
The current context of execution. The context in which values and expressions are “visible” or can be referenced. If a variable or other expression is not “in the current scope,” then it is unavailable for use. Scopes can also be layered in a hierarchy, so that child scopes have access to parent scopes, but not vice versa.
恩~看起來好像很複雜…
在將MDN簡化後,作用域可以簡單理解:
- 變數的有效範圍,離開有效範圍的變數無法被存取
- 外部作用域無法取得內部作用域的變數,但內層的變數可以取得外層的變數
下面的例子可以看到函式中的變數無法在函式外獲取,並且該變數只存在於函式中。
function foo() {
var user = "廣志";
console.log(user); // 廣志
}
foo();
console.log(user); // Uncaught ReferenceError: user is not defined
JavaScript的作用域有哪些?
依據W3Shool的說明,JavaScript的作用域共有3種。
分別是:
w3schools JavaScript Scope
Scope determines the accessibility (visibility) of variables.
JavaScript has 3 types of scope:
Block scope
Function scope
Global scope
JavaScript的作用域在引入ES6之前只有全域作用域(Global Scope)和函式作用域(Function Scope)。
而在ES6中因為let與const的關係而有了區塊(Block scope)。
接下來由外向內看看這些作用域吧~
全域作用域(Global Scope)
全域作用域是JavaScript作用域的最外層,也就是大家常聽到的全域,
凡是在全域環境中宣告的變數或函式都可以在其他作用域中取得
如下面例子:
const num = 10; // 全域變數
function foo() => {
console.log(num); // 10
}
console.log(num); // 10
例子中可以看到在全域宣告的const變數,可以在函式與區塊作用域中取得其值!
這邊要注意,在變數宣告一文中有提到var、let與cosnt有各自的作用範圍,但當他們皆在全域宣告時,作用範圍便屬於全域,也就是全域變數。
函式作用域(Function Scope)
每當建立一個函式,都會創建一個新的函式作用域。函式內部宣告的變數不能從函式外部訪問。
不論是var、let或const宣告的變數,當他們在函式中宣告時,皆屬於函式作用域。
如下面的例子:
function foo(){
var num = 10; // function scope
}
console.log(num); // Uncaught ReferenceError: num is not defined
function foo(){
let num = 10; // function scope
}
console.log(num); // Uncaught ReferenceError: num is not defined
區塊作用域(Block Scope) (ES6)
由於ES6引入了let與const變數,這2個變數宣告方式提供了區塊作用域。
區塊作用域的範圍只存在於{},例如if或for的大括號中。
區塊作用域與函式作用域相同,函式內部宣告的變數不能從函式外部訪問。
如下面例子:
function foo1() {
if (true) {
const user = "花爸";
}
console.log(user);// Uncaught ReferenceError: user is not defined
}
foo1();
函式作用域等於區塊作用域嗎?
說到這邊有人會問:既然區塊作用域範圍在大括號之間,難道函式作用域也是區塊作用域的一種嗎?
雖然2者還是有點不同,但可以這麼認為,畢竟區塊作用域是ES6之後才出現的概念,過去ES5的作用域只有全域與函式作用域。
差別在於let與const宣告的變數提供了區塊作用域,而var宣告的變數則不會有區塊作用域。
也就是說,let或const的宣告,讓這個變數在函式中具有區塊作用域。
看下面例子:
function foo1() {
if (true) {
const user = "花爸";
}
console.log(user);// Uncaught ReferenceError: user is not defined
}
foo1();
//----------------------------------
function foo2() {
if (true) {
var user = "花媽";
}
console.log(user);//花媽
}
foo2();
例子中foo1函式中使用const宣告了變數,所以if大括弧內為區塊作用域。
foo2中使用var宣告的變數則沒有,所以可以在if外取得user的值。
還記得變數宣告一文中提到var作用範圍屬於函式嗎?這也是建議宣告變數的方式多使用let與const的原因
作用域為何重要?
可以有私有變數
如果沒有作用域,在程式中我們可以任意訪問與存取所有變數,這會造成變數間可以相互影響,使程式出現不預期的執行結果。
避免命名衝突
呈上一點,由於變數可以被任意訪問與存取,所以我們無法在不同地方或目的使用相同名稱的變數。
有了作用域可以讓我們明確規範變數的使用範圍,並在程式中不同的位置使用相同名稱的變數,使寫出來的程式更具可讀性與可維護性,這部分的延伸便是閉包。
作用域鏈 (Scope Chain)
先看這個例子:
var num = 10;
function foo() {
console.log(num); // 10
}
foo();
當無宣告的變數在函式內找不到宣告的變數時,會從內到外尋找變數,如果最終在全域沒找到則在拋出錯誤的行為。
這種由內向外尋找的行為便是作用域鏈(Scope Chain)。
總結
這邊總結這篇文章我們獲得了什麼?
作用域是什麼?
- 變數的有效範圍,離開有效範圍的變數無法被存取
- 外部作用域無法取得內部作用域的變數,但內層的變數可以取得外層的變數
JavaScript作用域有哪些?
- 全域作用域(Global Scope)
- 函式作用域(Function Scope)
- 區塊作用域(Block Scope)
作用域為何重要?
- 可以有私有變數
- 避免命名衝突
作用域鏈是什麼?
自由變數由內向外搜尋的行為
參考資料
JavaScript – The Complete Guide
w3schools-Scope
MDN-Scope
重新認識 JavaScript: Day 10 函式 Functions 的基本概念
你懂 JavaScript 嗎?#12 函式範疇與區塊範疇
[JS] Scope 作用域
如果對文章內容有任何問題或是錯誤,歡迎在底下留言讓我知道: )
Greetings! Very helpful advice in this particular post! It is the little changes that make the most significant changes. Thanks for sharing!