變數宣告
宣告 (declaration) 是告訴電腦需要一個記憶體空間來存放資料,類似置物櫃的概念。
let / const
let可以隨時賦值 (assign) 改變,宣告時可以不賦值,內容為undefined。const指不會變動的內容 (constant),宣告後必須賦予初始值,且不能改變內容。
let a; // a 為 undefined
const b; // 報錯,const 在宣告時必須賦值
const c = 1;c = 2; // 報錯,不能改變 const 的內容var
ES6 前只有 var 這個宣告方式,但語法規定寬鬆,容易造成流程上的意外。
可以重複宣告
有碰過其他語言的話一定會覺得重複宣告是非常奇怪的事:
var a = 1;
console.log(a); // 1
var a = '我被重新宣告竟然沒有報錯太扯了吧';
console.log(a); // '我被重新宣告竟然沒有報錯太扯了吧'作用域
let 或 const 的作用區間以大括號為限,大括號以外就無法存取變數,迴圈、函式,或任一組大括號等都符合這個規則:
{ let a = 1; const b = 'hello';}
console.log(a, b); // 報錯,a 與 b 未宣告大部分的語言也遵循這種以大括號為限來設計的,但是 var 的行為不同:
var a = 1;
for (var i = 1; i < 5; i++) { a += i;}
console.log(a); // 11console.log(i); // 5迴圈跑完後可以查看 a 的值是 11,這是正確的,但是連 i 的值都可以存取!
這是因為 var 真正的作用域是在函式內部,而 for 只是多個大括號的連續執行,並不是函式。
以上特性都有機率產生全域污染,例如:取出預期之外的值、無意間修改了同名的資料。
所以現今幾乎不推薦使用 var 宣告。
必考題
for (var i = 0; i < 3; i++) { setTimeout(() => { console.log(i); }, 0);}最終會印出 3 次 3。
setTimeout 是 Web API,在事件循環 (Event Loop) 中會先放到瀏覽器程序的排程空間 (Callback Queue),不會影響主程序。
所以 setTimeout 給予時間參數 0 ,代表它是立刻進入排程空間,不是馬上執行,所以主程序的運算繼續執行,到下一個循環 setTimeout 的內容:
var i = 0;
{ i++; // i 為 1}{ i++; // i 為 2}{ i++; // i 為 3,迴圈終止}
console.log(i);console.log(i);console.log(i);變數一定要經過宣告嗎
沒有任何宣告的關鍵字,直接寫出變數名稱,會變成全域可以存取的值:
function foo() { a = 1;}
foo();
console.log(a); // 1console.log(window.a); // 1foo 執行完後仍然可以找到 a,此時 a 是全域物件 window 的一個屬性 (property),用 window.a 可以取出來,所以 a 目前不是一個獨立的變數。
屬性與變數最重要的差異在於,屬性可以使用 delete 運算子刪除:
function foo() { a = 1;}
foo();
delete a;console.log(a); // Uncaught ReferenceError: a is not defined
let b = 2;delete b;console.log(b); // 2delete a 之後印出 a 會報錯,因為 a 是屬性,已經確實被刪除。
使用 let 宣告的 b 還是可以印出來,因為 delete 運算子對變數不生效(但是不會報錯)。
小結
| var | let | const | |
|---|---|---|---|
| 作用域 | 函式 | 大括號 | 大括號 |
| 初始值 | 非必須 | 非必須 | 必須 |
| 重複宣告 | 可以 | 不行 | 不行 |
- 留意
var的特殊機制與作用域問題 - 未使用關鍵字宣告的變數會變成全域物件的屬性
delete只對屬性生效,不對變數生效