Jeff的隨手筆記

學習當一個前端工程師

0%

『Day -3』 作用域(scope)

我們在昨天有提到scope 這個詞,那這個詞到底是什麼意思呢?為什麼block scope一出來就讓我們放棄用var來宣告呢?讓我們看下去吧

Lexical environment 詞彙環境

在講到scop以前,我必須先提一下一個很重要而且相關的概念,那就是Lexical environment (詞彙環境)。

Where you write something is important

你在哪裡寫東西在JS裡是非常重要的,例如我宣告一個變數在function的內部或外部就會差非常多。

當程式透過引擎轉譯給電腦執行時,會因為它應對的記憶體位置影響他和其他function的互動,因此 Lexical environment 就是指開發環境中程式碼的位置。

scope是什麼

MDN是這樣解釋的:

The scope is the current context of execution in which values and expressions are “visible” or can be referenced. If a variable or expression is not in the current scope, it will not be available for use. Scopes can also be layered in a hierarchy, so that child scopes have access to parent scopes, but not vice versa.

簡單來說Scope就是變數的有效範圍,離開有效範圍的變數無法被存取,另外父層作用域無法取得子層作用域的變數,但子層作用域的變數可以取得父層作用域的變數。

我們來看一下下面這個例子會更清楚:

1
2
3
4
5
6
7
8
let a = "Hello World"
function b() {
let c = "Hi"
console.log(a)
}

b()
console.log(c)

結果是:

1
2
Hello World
ReferenceError: c is not defined

由於變數 c 是在函式 b() 內部宣告的,這表示它的作用域僅限於函式內部。一旦函式 b() 執行完畢,變數 c 的作用域也就結束了,所以我們無法在函式外部訪問它。

Scope的分類

在 MDN 上把他分成這幾種:

  • Global scope(全域作用域): The default scope for all code running in script mode.
  • Module scope(模組作用域): The scope for code running in module mode.
  • Function scope(函式作用域): The scope created with a function.

In addition, variables declared with let or const can belong to an additional scope:

  • Block scope(區塊作用域): The scope created with a pair of curly braces (a block).

接下來我們就針對這些作用域來簡單介紹一下

Global scope

Global scope 中文為 全域作用域 是JavaScript作用域的最外層,也就是大家常聽到的全域

1
2
3
4
5
6
let a = "Hello World"
function b() {
let c = "Hi"
console.log(a)
}

以這個案例來說,a就是屬於全域變數。
另外瀏覽器中BOM的window物件,我們也會稱作全域變數。

Module scope

過去 javascript 沒有原生的模組系統,早期會透過 IIFE 及 clousure 避免私有變數外溢,es6 後加入了 esm 作為原生的模組語法。

在 esm 中,每一個檔案都是一個模組,有自己的作用域 (scope),模組的作用域可以幫助我們

  • 相較於單一環境,能夠減少命名衝突
  • 擁有私有變數及方法,避免外溢至全域環境
  • 依照單一職責原則 (single responsibility principle) 劃分函式或模組,提升可維護性

這意味著在模組內定義的變數、函式和其他程式碼對於其他模組或全域作用域的程式碼而言是不可訪問的,除非明確地導出。

當您導入一個模組時,導入的程式碼僅在導入的模組範圍內可用。例如,如果您將一個模組導入到一個函式內,則導入的程式碼只能在該函式內部訪問,而在函式外部則無法訪問。

Function scope

就如同名字所說的,有效範圍只在函式內部,函式結束後,裡面的變數就無法取得了。

在ES6之前,JavaScript 變數有效範圍的最小單位是以 function 做分界的。

一樣,我們用案例來說明:

1
2
3
4
5
6
7
8
9
var x = 100;

function addHadler(y) {
var x = 20;
return x + y;
};

console.log( addHadler(50) );
console.log( x );

結果是:

1
2
70
100

由於我們在函式裡面再次定義了變數 x ,因此當我們在執行return x + y 時,x就會用我們重新定義的值。而我們在執行console.log( x ) 時,由於“父層作用域無法取得子層作用域的變數”,因此我們只能在往上尋找X,所以最後才會印出100這個答案。

Block scope

區塊作用域(Block Scope)是指變數(或函式)在一個區塊內部被定義,僅在該區塊內部有效,且在區塊外部無法訪問。區塊可以是由一對花括號 {} 所構成的,例如在條件語句(if、else)、循環語句(for、while)、函式內部等。

在區塊作用域中,變數在區塊的範圍內有效,不會影響到區塊外的同名變數。這有助於避免變數名稱衝突,提高程式碼的可讀性和可維護性。

在找資料時有看到這一段code,很清楚的解釋了var 跟 let、const 作用域的差別:

1
2
3
4
5
6
7
8
9
10
11
12
function checkScope() {
if (true) {
let a = '塊級作用域';
var b = '函式作用域';
} else {
// a 變數屬於 if 判斷式中的塊級作用域,在此處不可用
// b 變數屬於 check 函式的作用域,在此處可用
console.log(b); // 函式作用域
}
// a 在此處不可用,b 在此處可用
}
// a,b 在此處不可用

以上就是關於scope的簡單介紹,每一次複習都會有新的發現,就像我到今天才知道有Module scope 一樣,之前看過的資料好像都沒有提到這個><”