作用域(Scope)的差別
作用域(Scope)是指在程式碼中變數和函式的可訪問範圍
var 函數作用域(function scope)
- 變數在函數中的任何地方都是可讀取的。
- 如果在函數之外的地方嘗試訪問變數,將會顯示該變數不存在ReferenceError的錯誤
- 變數作用域說明
- 第一個 var a = 20(作用域:1-3行)
- 第二個 var a = 30(作用域:4-6行)
- 範例如下
1 | function funcA() { |
顯示如下
1 | Uncaught ReferenceError: a is not defined |
全域變數擁有全域作用域(Global Scope)
- var變數在函式(Function)外宣告時,該變數會自動成為全域物件屬性。
- let宣告的變數不會成為全域物件屬性。其作用域僅限於宣告它的區塊(block scope)或函式內部。
範例如下:
1 | <html lang="en"> |
- 將全域物件window印出,可以看到剛剛設定的a已經變成window的一種屬性
若跳過宣告的流程,宣告的變數也會變成全域物件屬性
範例如下:
1 | <html lang="en"> |
跳過宣告的情形下,變成Window全局屬性
let/const 塊級作用域(block scope)
- 變數在聲明的**區塊{}**(例如,if 語句、迴圈)內可讀取的
- 如果在區塊之外的地方嘗試訪問變數,將會顯示該變數不存在ReferenceError的錯誤
最簡單的定義方式就是單純{}
範例如下
1 | <html lang="en"> |
顯示如下
搭配以下範例說明let/const 變數作用域
- const Title = “數字太大了”(作用域:2-5行)
- 範例如下
1 | function funcA(Value) { |
顯示如下
1 | if區域 數字太大了 |
- Title作用域在if的括號之內(紅色框框),因此 if 外層的console.log(“我是funcA的最下面”, Title)無法正確讀取到Title變數,出現ReferenceError錯誤
const & let 差異
let與const重新賦值差別
- let可以重新賦值,案例如下
1 | let x = 10; // 宣告一個let變數 |
- const不可以重新賦值,案例如下
1 | const x = 10 // 宣告一個const變數 |
顯示如下
1 | TypeError: Assignment to constant variable. |
const可針對物件型別內容物修改
- const宣告的變數,無法更改原始型別,但可以修改物件型別的屬性
範例如下(物件型別屬性修改)
1 | const a = { |
顯示如下
1 | { name: 'Antonio' } |
const常數變數宣告就必須賦值
範例如下(無賦值發生錯誤)
1 | const a |
顯示如下
1 | SyntaxError: Missing initializer in const declaration |
作用鍊(Scope chain)
當使用變數時,依序從當前作用域開始,一層一層往外尋找是否有符合其作用域的變數。這個搜尋的過程形成了作用鍊
試著將上圖的程式碼改成console.log(a + b + c + d)顯示如下
- 因為已經到最外層還是找不到宣告變數d的地方,因此出現ReferenceError錯誤
1 | Uncaught ReferenceError: d is not defined |
for迴圈的使用差異
- 情境:使用setTimeout定時器,模擬異步事件。查看透過var及let變數在迴圈中的變化
var定義的index,會彼此共用影響
- var結束的時候,index = 10(全部的index都會被影響)
1 | for (var index = 0; index < 10; index++) { |
顯示如下
let是重新宣告出來,彼此互不影響(只在單一區塊作用)
1 | for (let index = 0; index < 10; index++) { |
顯示如下
提升(Hoisting)的差別
JavaScript編譯階段將變數和函式的宣告存入記憶體的概念,使函式和變量的宣告看起來好像被提升到作用域的頂部(實際程式碼的順序不變),但賦值的動作並沒有提升
var變數宣告時會自動存入記憶體並初始化定義undefined
範例如下:
1 | console.log(x) |
顯示如下
1 | undefined |
實際上瀏覽器看到的是類似以下的順序(並不會真的改變程式碼順序):
1 | var x |
- 執行順序上,var變數會優先提升至作用域的頂部並初始化定義為undefined
const/let變數提升時會陷入暫時性死區(Temporal Dead Zone, TDZ)
範例如下:
1 | console.log(x) |
顯示如下
1 | ReferenceError: Cannot access 'x' before initialization |
透過不同方式更確定let的區塊性提升
範例如下
1 | var a = 10 |
- 因為let的a變數提升,導致console的結果並未顯示10
顯示如下
1 | ReferenceError: Cannot access 'a' before initialization |
重複宣告
var變數可以重複宣告
範例如下
1 | var a = 10 |
顯示如下
1 | 20 |
- 同樣變數的宣告會被替代,因此最後印出來的是最後一個20
let變數不可以重複宣告
範例如下
1 | let a = 10 |
顯示如下
1 | SyntaxError: Identifier 'a' has already been declared |
- 同一區塊內不可以宣告同樣的變數