0%

ES6 變數宣告與全域環境

JavaScript

一種被廣泛使用的程式語言,好上手也最容易踩雷,從 ES5進化到 ES6後增加了一些變數宣告方式與不少的語法糖,其歷史沿革已有很多前輩說明,為了方便之後描述,以下 JavaScript簡稱為 JS。

JS 變數宣告

var

在 ES5時期,要宣告一個變數會使用 var作為開頭,但它有個缺點,就是它會建立在全域,在後續開發上會讓造成混淆,像是以下例子:

1
2
3
4
5
var a = 1
console.log(a) // 1
console.log(window.a) // 1
// window是 JS的最上層,也就是全域
// 它有很多 JS內建的事件與函式

透過瀏覽器(開發者模式)來看,會發現在 window中找得到 a這個變數,這很可能讓後續寫下的變數或者函式抓到同樣的變數(註1),進而造成不必要的錯誤。不過,它不是不能用,根據實際情況改變寫法才是我們要的。
順帶一提,若有安裝 ESLint這類的檢查工具,這個宣告方式還是會被建議改成 let。

註1 : 若函式裏頭未宣告且命名一個變數並賦予值,那麼此變數就會在全域環境中建立一個該變數的名稱(這個行為是 Hoisting,中文名為提升)。

1
2
3
4
5
6
7
8
9
var b = function (){
a = 2
var c = 22
}
b()
console.log(a) // 2
console.log(c) // c is not defined
// 執行 b函式時,全域環境中建立了名為 a的變數
// 而變數 c是在函式內宣告,因此在"全域環境"中找不到變數 c

let

在 ES6中,新增了 let與 const,這邊先介紹 let。

特性

  • 只在某區塊(例如函式)中有作用
  • 無法重複宣告同樣變數名稱
  • 變數的值可改變

範例

為了解決 var所帶來的困擾,使用 let來做為變數宣告會是一個比較好的方式,它的作用範圍被限制在某個區塊中,就算不在函式中宣告,在 window中也找不到利用 let所宣告的變數。以下為範例 :

1
2
3
let a = 1
console.log(a) // 1
console.log(window.a) // undefined

const

特性

  • 無法重複宣告
  • 變數的值不可改變
  • 若賦予的值為陣列或物件,可以改變其內部屬性

使用 const宣告的變數為常數,無法改變其值,類似唯讀。

全域環境

上述的範例中會看到 window.a,這其實是物件的寫法,利用 var來宣告變數時,會在 window中新增一個物件屬性,為什麼會提到物件? 因為在說明 let時,自己不禁有個疑問,let a 有宣告,但在全域中卻找不到,那它去哪裡了?

全域之複合環境

這裡似乎有相關的說明。目前自己的理解是,全域環境中是一個複合環境,包含 :

  • 物件環境 Object Environment
  • 聲明環境 Declarative Environment

    範例

    1
    2
    3
    4
    var a = 10
    var b = function(){
    console.log(a)
    }
    以表格來看的話,類似像這樣 :
    聲明環境 物件環境
    a 10
    b funciton()

而 JS是物件導向,以物件來看的話,

1
2
3
4
5
// 會在 Global內發現它們
{
a: 10
b: f()
}

瀏覽器中的 Scope

對於瀏覽器來說,當執行環境執行程式碼時,會在瀏覽器的 Scope產生 Global與 Script,而範例的執行環境就是全域環境,所以當變數沒有被告知用哪一種變數宣告,那麼就會根據 JS規則將此變數建立在 Global,若在全域環境下也用 var宣告,那麼就會是建立在 Global。
而使用 let與 const宣告時,它們會被建立在 Script,這就是為什麼無法在 window中找到的原因,因為它們的建立規則不是建立在 Global。

參考來源

  1. pvt5r486 - [JavaScript Weird] Day 2 — 全域執行環境與全域物件
  2. Ray - 淺談 var 與 let 的差異以及有無宣告變數的差異
  3. realdennis - 一次說清楚 JavaScript 中宣告的各種提升行為(var、function、let/const)
  4. Fun Lee - 关于 const 和 let 声明的变量不在 window 上