使用 var 宣告變數

var 從 ES5 就存在,也是 ES5 的代表 keyword,看到 var 就可以判斷為 JavaScript。

var 在 ES6 有了一些改變,重要性也不若以往,TC39 甚至建議完全不要使用 var,改用 letconst

Verson


ECMAScript 2015

Var


宣告變數於 function 內或 function 外 (global)。

  • Scope:為 execution context,分 function (function 內) 與 global (function 外),而非 {}
  • Auto Global:若沒 var 一個變數,會自動 升級 成 global 變數 (ES5 ok、但 ES6 廢除)
  • Undefined:有 var 但未指定值,就是 undefined
  • Hoisting:無論你寫在 function 內第幾行,都會在 code 執行 先宣告變數
  • Re-declare:若重新 var 一個變數,原來的值仍會存在

Scope


scope01.js

1
2
3
4
5
6
7
8
function x() {
var z = 2;
}

x();

console.log(z);
// ReferenceError

ES5 與 ES6 都無法執行。

z 的 execution context 為 function 內,所以 function 外部抓不到 z,會在 run-time 跳出 ReferenceError

scope02.js

1
2
3
4
5
6
7
8
z = 2;

function x() {
console.log(z);
}

x();
// ReferenceError

ES5 可執行,ES6 會噴 ReferenceError

在 ES5 允許 global variable 不使用 var 宣告變數,但 ES6 會啟動 strict mode,儘管是 global variable,也一定要使用 var

Auto Global


auto-global.js

1
2
3
4
5
6
7
8
function x() {
y = 1;
}

x();

console.log(y);
// ReferenceError

ES5 可執行,ES6 會噴 ReferenceError

y 在 ES5 會自動升級為 global 變數,還是會印出 1,但 ES6 會啟動 strict modey 無法升級成 global 變數,會在 run-time 跳出 ReferenceError

Undefined


undefined01.js

1
2
3
console.log(a);
console.log('still going...');
// RefferenceError

ES5 與 ES6 都無法執行。

Run-time ReferenceError,因為 a 沒有 var 宣告。

undefined02.js

1
2
3
4
5
var a;
console.log(a);
console.log('still going...');
// undefined
// still going...

ES5 與 ES6 都可執行。

a 只宣告但沒有給值,因此為 undefined

ECMAScript 對於變數,沒有 預設值,也不是 null,而是特有 undefined

有些瀏覽器為 ""

Hoisting


1
2
bla = 2;
var bla;

但實際執行時為

1
2
var bla;
bla = 2;

也就是無論是 ES5 或 ES6,都會自動將 var 移到程式碼的最前面先執行。

hoisting01.js

1
2
3
4
5
6
7
8
9
function foo() {
console.log(bar);
var bar = 111;
console.log(bar);
}

foo();
// undefined
// 111

實際執行時

1
2
3
4
5
6
7
8
9
10
function foo() {
var bar;
console.log(bar);
bar = 111;
console.log(bar);
}

foo();
// undefined
// 111

無論 ES5 或 ES6,因為 var bar 會被 Hoisting 到 function 的最前面,因此為 undefined

因為 var 會 Hoisting,導致 JavaScript 的程式碼風格與 C# 迥異:

  • JavaScript 會變數宣告在 function 內一開始
  • C# 會在使用才宣告變數

hoisting02.js

1
2
3
var x = y, y = 'A';
console.log(x + y);
// undefinedA

實際執行為

1
2
3
4
5
6
var x;
var y;
x = y;
y = 'A';
console.log(x + y);
// undefinedA

因為 var y 會先被 Hoisting,所以 x = y 時,y 還是 undefined,因此 x 也是 undefined

實務上建議一行只 var 一個變數

Redeclaration


redeclaration01.js

1
2
3
4
5
6
7
var x = 2;
console.log(x);

var x;
console.log(x);
// 2
// 2

ES5 與 ES6 都可執行。

實際執行為

1
2
3
4
5
6
7
8
var x;

x = 2;
console.log(x);

console.log(x);
// 2
// 2

因此 var x 被 Hoisting,所以結果都是 2

redeclaration02.js

1
2
3
4
5
6
7
var x = 2;
console.log(x);

var x = 3;
console.log(x);
// 2
// 3

ES5 與 ES6 都可執行。

實際執行為

1
2
3
4
5
6
7
8
var x;
x = 2;
console.log(x);

x = 3;
console.log(x);
// 2
// 3

由於 Hoisting 機制,JavaScript 允許 re-declare。

C# 無法這樣寫,compile 就會報錯

Babel 可編譯也可執行,SonarQube 會抓出來

但實務上不建議使用 re-declare 機制

Conclusion


  • 在 ES6,無論 function 內的 variable 或 global variable,一律要使用 var,否則會噴 ReferenceError
  • JavaScript 有獨特的 undefined,只要變數沒有給定值都是 undefined
  • 由於 JavaScript 獨特的 Hoisting 機制,導致變數宣告都集中在 function 的最前面,與 C# 不同
  • 由於 Hoisting 機制,JavaScript 允許 re-declare,與 C# 不同,但 SonarQube 會抓到錯誤,且實務上不建議這樣寫

Sample Code


完整的範例可以在我的 GitHub 上找到

Reference


JavaScript MDN, var

2018-09-26