0%

JS特訓-DAY37-let、const

前言

有了「區塊域」的概念,就能避免掉使用 var 會汙染全域變數了

ES6 許多語法與現有的觀念有很大的關係,其中是為了改進現有的語法,
這次就要來介紹 letconst
這些語法改進目前很多問題,最大的感受在於 Promise(),他可以把非同步的語法處理得更好。

但 ES6 語法依然還有瀏覽器不支援,所以還會利用 BABEL 做編譯,但 ES6 在開發上的確會省下不少時間,
而且編輯器外掛直接可以安裝 BABEL,是提升 JS 程式碼品質的最佳利器!

注意:目前還不是所有語法支援,詳細可參考 MDN 或 Can I use

let、const、var

  • var 是函式作用域(function scope)
  • let 與 const 是區塊作用域 (block scope),區塊就是指在大括號內 { }
  • const 是宣告常數,不能再作修改

let 跟 var 的不同

var 變數的範圍在 function,而 let 的作用域在 blockblock 意指 { } 這個符號,
除了 function 以外 iffor{ } 都屬於 let 的作用域。

從下面的範例可以看到, Bob 這個變數在兩者的結尾是不同的,因為 { } 所定義的空間並不同。

function varMing () {
  var ming = 'Bob';
  if (true) {
    var ming = 'May';  
    // 這裡的 ming 依然是外層的 Bob,所以 Bob 即將被取代
  }
  console.log(ming);  // 'May'
}

function letMing () {
  let ming = 'Bob';
  if (true) {
    let ming = 'May';  
    // 這裡的 ming 是不同的,只有在這個 if block 才有作用
  }
  console.log(ming);  // 'Bob'
}
varMing(); letMing();

let 與 for loop

大多時候我們不會這樣寫,常見的習慣中我們會將變數宣告放在 function 的前方

for (var i = 0; i < 10; i++) {
  console.log(i);
  setTimeout(function () {
    console.log('這執行第' + i + '次');
  }, 10);
}

上面範例你可能會以為答案是「這執行第 0 次, 這執行第 1 次, 這執行第 2 次… (延續 10 次)」,
但實際執行時會發現答案其實會是 「這執行第 10 次… 」(延續 10 次),因為 var 會直接將 i 宣告成全域變數,不斷透過 for 迴圈累加,在 setTimeout 實際執行時只會拿到 10 這個數字。

所以此段要正確地執行,可以使用 let 宣告 i 這個變數, i 會被緊緊的鎖在 for... 後方的 { } 內。
這樣就會正確的出現,因為 let 是屬於 block 變數。

for (let i = 0; i < 10; i++) {
  console.log(i);
  setTimeout(function () {
    console.log('這執行第' + i + '次');
  }, 10);
}

// 這執行第0次
// 這執行第1次
// 這執行第2次
// 這執行第3次
// ...

此外,var 在全域下的變數會直接在 window 上,可以在這裡使用 console.log() 看看兩者之間的差異:

// 這段宣告擇一執行
var mom = "老媽";
let mom = "老媽";
console.log(window.mom);
// var 會出現 "老媽"
// let 會出現 undefined

這也是 let 的一大特性,他並不會讓整個 window 物件掛上在全域環境所宣告的變數,對於愛乾淨不喜歡污染全域的開發者會是一大福音。

const

const 宣告的是常數,常數在被宣告時就務必要指定給值,並且不能再被更改,不然會產生錯誤。
如果是需要變更的數值則改用作用範圍較小的 let 做宣告,來減少錯誤出現的機率,Ex:使用 funciton 或是 for 迴圈,可以使用 let。

來看簡單的例子:

let a = 1;
a = 2;
console.log(a); //2

這可以理解。如果換成 const

const a = 1;
a = 2;
console.log(a); //2

在開發人員工具,就會出現跳錯的訊息,
Uncaught SyntaxError: Identifier 'a' has already been declared
所以只要是不能變更的資訊,就可以使用 const 來宣告。

const 物件與陣列資料

用 const 宣告物件與陣列的資料時是可以改變其內容的,假設今天 const 一個物件,然後修改他,會發現內容的值變動了:

const obj = {
  url: "http://xxx.com",
};
obj.url = 1;
console.log(obj.url); //1

倘若不希望使其資料變動,可以加上一段語法 Object.freeze,就可以鎖住資料,不會被修改

freeze: 凍結的意思

程式碼的位置要放在物件或陣列資料的後方:

const obj = {
  url: "http://xxx.com",
};
Object.freeze(obj); //將 obj 的資料或屬性鎖定,便不能修改
console.log(obj.url); //'http://xxx.com'
obj.url = 1;
console.log(obj.url); //'http://xxx.com'

陣列的鎖定資料語法也相同是 Object.freeze

let 與 const 的共同特性

var 有向上提升的特性, letconst 沒有

使用 var 宣告變數的時候具備向上提升的功能,所以如果是這樣宣告,結果會如下:

console.log(a); //undefinded
var a = 1;
console.log(a); //1

因為 var 有向上提升的功能,在 JavaScript 是從上讀取到下的方式,其實會變成這樣的讀取:

var a; //尚未賦予 value
console.log(a); //undefinded
var a = 1;
console.log(a); //1

但如果將 var 改成 let 或是 const 都會變成跳錯的資訊。

Uncaught ReferenceError: Cannot access 'a' before initialization

在同個區塊 { } 上不能重複命名

  • var 可以重新賦予其值
var a = 1;
var a = 2; // var 可以重新賦予
  • letconst 不能重新賦予值
let a = 1;
let a = 2; // a 已被賦予值
const b = 1;
const b = 2; // b 已被賦予值

const 與 let 不會在全域變數 (window)裡面

這裡可以使用開發人員工具,在 console 輸入 window 檢查。

let a = 1;
const b = 1;
var c = 1;

我們會發現 a 與 b 都不會出現在 window 裡,但是 c 會出現在 window 裡,這是因為 c 是全域變數。
這邊就能回到我們一開始所說的,使用 letconst 能避免掉使用 var 會汙染全域變數的副作用囉!