You are currently viewing [筆記]-JavaScript 作用域與作用域鏈是什麼?關於作用域的4個觀念

[筆記]-JavaScript 作用域與作用域鏈是什麼?關於作用域的4個觀念

作用域(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 作用域

如果對文章內容有任何問題或是錯誤,歡迎在底下留言讓我知道: )

This Post Has One Comment

發佈留言