---
# System prepended metadata

title: Javascript  Scope 與 Hoisting
tags: [frontend learning]

---

# Javascript  Scope 與 Hoisting
在自學 JS 的過程中，除了基本的語法以外，還會被許多的專有名詞搞得一頭霧水，學習初期非常常見到的兩個名詞**作用域**以及**hoisting**。

## 什麼是 scope ?

我們在之前的 [JavaScript 宣告變數與運算子](https://hackmd.io/@un/HkNYFvvX3)這篇文章有提到 Lexical environment(詞彙環境) 與 execution context(執行環境)，可以先複習一下再往下學習！
>Lexical environment(詞彙環境) - 你把這段程式碼寫在哪？

scope 可以說是變數的取用範圍（作用域），意思是變數可以被使用，以及被看到的有效範圍，而詞彙環境就是決定了這個範圍。

在 JS 中可以大致分為以下三種作用域：
### scope 的類別

1. Global scope
指不在函式或是區塊內宣告，是全區的作用域，意指可以在任意地方取用變數的範圍，也可稱為"全域變數"

```
const value = "Hello world !" //這是全域變數
```

2. Local scope(function level scope)
指在特定範圍內才可以取用變數的作用域，也可稱為"函式作用域"(function level scope)，若函式外有一個相同名稱的全域變數，會優先取用函式內的變數。

```javascript=
const value = "Hello world !" //這是全域變數
const fun = function(){
    const value = "Hi world !" //這是區域變數(區域作用域)
    console.log(value) //會顯示 Hi world !
}
console.log(value) // 會顯示 Hello world ! // 變數會使用全域變數，無法讀取到 function 內的 value

```

3. block scope(ES6)
指存在於 `{}` 中才可以取用變數的作用域，像是在 function 中的 `if `或是`for` 語句中的{}，block scope 僅適用於同是 ES6 的 `let` . `const`，若使用 `var` 則無法形成 block scope。下方可以看見，在 for 語句的大括號中，可以 log 出 num 的值，但是在大括號外就變成 not defined。

```javascript=
const fun = function(){
    for (let i = 0; i< 3; i ++){
        let num = 10;
        num += i
        console.log(num) //這裡會顯示 10, 11 ,12 (這是 block scope)
    }
    console.log(num)// 會顯示 "Uncaught ReferenceError: num is not defined"
}
fun()
console.log(num)// 因為上方出現 error 則本行不會再執行

```

- 若使用 `var` 則無法形成 block scope

```javascript=
const fun = function(){
    for (var i = 0; i< 3; i ++){
        var num = 10;
        num += i
        console.log(num) //這裡會顯示 10, 11 ,12 (這是 block scope)
    }
    console.log(num)// 會顯示 12 (var 無法形成 block scope)
}
fun()
console.log(num)// 會顯示 "Uncaught ReferenceError: num is not defined"(var 可以形成 function level scope)
```

### scope chain 範圍鏈

scope chain 又稱作是範圍鏈，指的是函式會先查找作用範圍內的變數，若沒有，則會繼續往外查找，最後會查找到全域作用域中的變數。

- 從下方程式碼可以看見，因為在 function addA 中將變數 num 的值修改為 100，所以在作用域中查找到 num 的值就是 100
- 而在 addA 中呼叫 addB function 卻不是呈現 100 ，這是因為在 JS 中是使用 Lexical Environment (詞彙環境)。你會發現 addA 以及 addB 兩個函式沒有誰包著誰，根據詞彙環境下，由 addB 的物理位置往外查找，則會查找到全域環境，所以 num 的值是 10，這也就形成了一個 scope chain

```javascript=
let num = 10;
addA();
function addA() {
  let num = 100;
  console.log(num); // 這裡會顯示 100
  addB();// 這裡會顯示 10
}
function addB() {
  console.log(num);
}

```

## 什麼是 Hoisting ?

Hoisting(提升)，就是讓某些類型的變數可以在宣告變數之前使用， JS 會先幫這些為宣告的變數留一個位置，也就是先幫這些變數保留記憶體（或可以說成，替他們先保留了一個位置）。

- 具有 hoisting 的變數有：

1. function declaration (actual value)
2. var variables (undefined)

```javascript=
 //calling function before define function
 console.log(addNum(10,100)) // you can get the right value 110
// this is function declaration
 function addNum(a,b){
     return a+b
 }

// hoisting of var
 console.log(num) // you will get undefined
 var num = 100;
```

3. let and const (TDZ(Temporal Dead Zone)).(uninitialized)
其實 let, const 也是會產生 hoisting, 但是因為預設值不同，會產生暫時性死區，引發錯誤。

```javascript=
console.log(book) 
// Uncaught ReferenceError: Cannot access 'book' before initialization 
let book = "10" 

 console.log(num2)
// Uncaught ReferenceError: Cannot access 'num1' before initialization
 const num2 = 100;
```

- 不具有 hoisting 的變數有 :

1. function expression
2. arrow function

```javascript=
// arrow function  don't have property of hoisting
 console.log(addNum1(10,100)) 
// you will get Cannot access 'addNum2' before initialization
 let addNum1 = (a, b)=> a+b;

// function express don't have property of hoisting
console.log(addNum3(10,100)) 
// you will get Cannot access 'addNum3' before initialization
const addNum3 = function(a,b){
    return a+b
}
```

## 參考資料
- [[JavaScript] Javascript 的作用域 (Scope) 與範圍鏈 (Scope Chain)：往外找](https://medium.com/itsems-frontend/javascript-scope-and-scope-chain-ca17a1068c96)
- [JS 原力覺醒 Day04 - Function Scope / Block Scope](https://ithelp.ithome.com.tw/articles/10217481)
- [[week 16] JavaScript 進階 - 什麼是閉包？探討 Closure & Scope Chain](https://hackmd.io/@Heidi-Liu/note-js201-closure)

###### tags: `frontend learning`