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