Skip to main content

[JS] JavaSript scope

info

以下內容大多來自 Web Dev Simplified Blog Post

針對範例有進行略微的修改以及加上其他參考文章的詮釋

scope(範圍)是一個很重要的觀念,我們要知道每個程式都有其作用的區域,就是他的 scope。有時候,當我們把 scope 搞混的時候,就會出現預期之外的錯誤。

The best way to think of scope is as a partition to help you separate different parts of your code from one another.

const outer = "out";

function test() {
const inner = "in";
console.log(outer, inner);
// Prints: "out", "in"
}

test();
console.log(outer, inner);
// Throws Uncaught Reference Error: inner is not defined.

在上面的範例中,我們可以看到外層跟內層都定義了變數,在內層的可以印出內外兩層的變數,而在外層卻無法印出內層的變數。

接著,我們仔細地去看不同的 scope 來去了解更多,有以下四種

  1. Global Scope
  2. Module Scope
  3. Block Scope
  4. Function Scope

Global Scope

我們要透過 HTML/JS 檔案來了解 global scope

index.html
<script src="script.js"></script>
script.js
const a = 1;
console.log(a);
// Prints: 1

在上面的程式碼,我們建立 index.html,裡面連接到 script,在 global scope 中定義了a變數,要注意的是這個變數可以被整個網頁使用,通常這樣是會帶來一些便利性但在之後的維護會變得困難,我們看以下程式碼

index.html
<script src="script.js"></script>
<script src="script2.js"></script>
script2.js
console.log(a);
// Prints: 1

透過上面程式碼,我們知道script2.js也可以使用script1.js定義的a變數,因為它可以在任何地方被使用,這會在應用程式持續擴大時產生難以追蹤程式碼的問題。

Module Scope

那要如何避免上面的問題呢?我們可以透過type=module來讓每個 script 有自己的 module scope

index.html
<script src="script1.js" type="module"></script>
<script src="script2.js" type="module"></script>
script1.js
const a = 1;
console.log(a);
// Prints: 1
script2.js
console.log(a);
// Throws Uncaught Reference Error: a is not defined

module scope 解決了 global scope 可能會產生的溯源問題,因此是一種常使用的定義範圍。

Block Scope

block scope 總是會被 curly braces包裹住,每個內都有著不同的範圍。常見的像是 function、if statements、for loops 都會創造出自己的 block scope。

function test() {
const funcVar = "Func";

if (true) {
const ifVar = "If";
console.log(funcVar, ifVar);
// Prints: "Func", "If"
}

console.log(funcVar, ifVar);
// Throws Uncaught Reference Error: ifVar is not defined
}

上述範例有兩個 block scope,test函式跟if承述式。每個 block scope 都可以呼叫定義在內的變數,但是不能呼叫到裡面的變數;而在裡面可以呼叫到外層定義的變數。

The important takeaway with block scope is anytime you have curly braces you create a new block scope that has access to all scopes it is inside of, but none of the scopes inside it.

Function Scope

function scope 和 var變數宣告有關,var定義的變數可以在整個 function 內使用,他可以跨過的 block scope。我們可以把一個 script 檔案看成是一個 function。

codepen: https://codepen.io/stevetanus/pen/MWBjdGQ

var funcVar = "ScriptVar";
function test() {
var funcVar = "Func";
if (true) {
var ifVar = "If";
console.log(funcVar, ifVar);
// Prints: "Func", "If"
}

console.log(funcVar, ifVar);
// Prints: "Func", "If"
}
test();
console.log(funcVar); // Prints: "ScriptVar"
console.log(ifVar); // Prints: Uncaught ReferenceError: ifVar is not defined

從上述範例,我們可以看到兩個 function,一個是最外面的 script,一個是test,裡面各定義了funcVar,會印出不同的結果,而ifVar會被 hoisting(提升)到 function scope,所以在test函式中,也可以印出ifVar的結果。

結論

最常使用的是module scopeblock scope,而我們在 ES6 之後,盡量(絕對)不要再使用var來進行變數宣告,以免發生預期外的錯誤,請大量使用letconst

參考資料