# 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`