iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 3
6
Modern Web

重新認識 JavaScript系列 第 3

重新認識 JavaScript: Day 03 變數與資料型別

上一篇文章我們簡單介紹了 JavaScript 的歷史,那麼從這篇開始將進入語法基礎篇。 JavaScript 的語法其實大量借鑒了 C 語言以及其他類似 C 語言 (如 Java) 的語法,所以過去如果你也寫過這種類語言的話,相信可以很快上手。

今天要介紹的主題就是 JavaScript 的變數與資料型別。

變數

變數是用來儲存資料和進行運算的基本單位,可以將變數想像為一個盒子,用來存放資料。

在 JavaScript 中的變數宣告有其一定的規則,變數的第一個字母必須為英文字母、底線 _ 或是錢字號 $ ,後面可以是英文字母、底線 _ 或是錢字號 $ 以及數字。 變數名稱不可以是保留字 (Reserved Words) 與關鍵字 (keyword) [註1]。

需要注意的是,JavaScript 的語法是有區分大小寫的,也就是說,變數 appApp 在 JavaScript 會被認為是兩個不同的變數,而且自 JavaScript 1.3 之後開始支援 Unicode。

換句話說,你的變數名稱是可以用中文命名的,而且完全合法,但基於開發習慣還是盡量避免使用非英文字母、底線或是 $ 以外的字元來命名變數,用中文編碼的話,其他語言的系統開啟可能會變亂碼。

在 ES6 以前,變數在使用前,可以透過 var 這個關鍵字來宣告。

在 ES6 之後宣告「變數」與「常數」,除了 var 之外,還可以透過 letconst 做宣告,這部分我們之後講到 ES6 時再談。

由於 JavaScript 是個「弱型別」[註2] 的語言,變數本身無需宣告型別,型別的資訊只在值或物件本身,變數只用來作為取得值或物件的參考:

// 透過 var 宣告變數 n,因為沒有給 n 數值,此時 n 的內容為 undefined。
var n;

// 宣告變數 m 的同時也給了變數 m 值,此時 m 的內容為數字的 1。
var m = 1;

如果在沒有宣告變數的情況就要使用的話,就會出現 ReferenceError 的錯誤:

// 未宣告變數就去存取時
// 主控台會出現 Uncaught ReferenceError: hello is not defined
console.log(hello);

像這樣
https://ithelp.ithome.com.tw/upload/images/20171206/20065504Mu6xcAZp4r.png

但在開發的時候你可能會發現一件事,就是即便你的變數在事前沒有透過 var 做宣告的時候,仍然可以給定變數初始值,但強烈不建議這麼做。

要記得一句話,所有沒有透過 var 宣告的變數都會自動變成全域變數

什麼是「全域變數」? 之後在介紹變數的作用範圍會再詳細解說。

// 對未宣告的變數 m 賦值
m = 1;

console.log(m);  // ok 好,會出現 1

註1: 關鍵字指的是 ECMAScript 所規定具有特定用途的英文單字,不能用來作為變數名稱使用。 而保留字則是雖然目前在 JavaScript 還沒有特殊用途,但在未來有可能會被拿來當關鍵字來使用,所以也不能作為變數名稱。 請參考 MDN 關鍵字與保留字列表

註2: 在程式語言中,會依照語言的型別系統 (Type system) 來分成「強型別語言」與「弱型別語言」兩種。 強型別指的是「程式所定義的變數型別等於變數在執行時期的型別」,也就是說,這類語言的變數在被宣告的時候,必須指定一種資料型別給它,如果對這個變數做了錯誤型別的運算時,就會出現錯誤,也可以利用編譯器提前做型別檢查,就可以減少在執行時期 (Runtime) 發生的錯誤。 弱型別語言則正好相反,雖然取得了語法簡潔的優點,但要注意型態轉換時產生非預期的問題。


變數的資料型別 (1)

剛剛說過,由於 JavaScript 是個「弱型別」的程式語言,嚴格來說,變數本身其實不帶有資料型別的資訊,其中的「值」或「物件」才有。 所以說在執行時期透過變數內容來參考至物件或值,才會得知此變數有什麼操作的方法。

變數沒有型別,值才有

在 ES5.1,JavaScript 內建的型別主要可以分成基本型別 (Primitives) 與物件型別 (Object) 兩大類。
而基本型別又分成 stringnumberbooleannullundefined 幾種 [註1],除了上述幾種之外,都可以歸類至物件型別 (Object)。

判斷型別的方式,可以透過 typeof 運算子來協助我們,但有時會有例外需要處理。

typeof  true;         // 'boolean'
typeof  'Kuro';       // 'string'
typeof  123;          // 'number'
typeof  { };          // 'object'
typeof  [ ];          // 'object'

// 下面兩個要特別注意,之後的篇幅會介紹到
typeof window.alert;  // 'function'
typeof null;          // 'object'

https://ithelp.ithome.com.tw/upload/images/20171206/200655045mWReenkfi.png
來源


string 字串

JavaScript 沒有 char (字元) 的概念,只有字串。 字串會用 ' ' (單引號) 或 " " (雙引號) 包夾住,兩者不可混用,意即用單引號開頭的,就要用單引號收合,反過來說也是。 單引號與雙引號的使用在 JavaScript 沒有什麼差異,依習慣使用即可。

var str  = '這是一個字串';
var str2 = "這也是一個字串";

但是,倘若要在單引號內包覆單引號,或是雙引號內包覆雙引號就會出現問題:

var str = 'Let's go!';    // error

如果改成這樣就可以:

var str = "Let's go!";    // OK

但如果有非用不可的時候,可以透過 \ (跳脫字元, escape character) 來處理:

var str = 'Let\'s go!';    // OK

如果遇到了多組的字串時,你可以用 + (加號) 來連接字串:

var hello = 'Hello, ' + 'World';

甚至是多行字串時,可以透過 \ (反斜線) 來繼續:

var hello = '這不是一行文 \
這是第二行 \
這是第三行';

要注意的是 \ 反斜線符號後面不能有任何東西,包括空白字元。


number 數字

JavaScript 只有一種數值的型別,就是 number,不管整數或帶有小數點的數字都是:

var a = 10;

var b = 12.34;

除了常見的整數與小數點一類的數字外,另外還有幾種特殊的數字:
Infinity (無限大) 、 -Infinity (負無限大) ,以及 NaN (不是數值,Not a Number)。

Infinity-Infinity 分別用來表示數學上的無限大與負無限大,一個正數除以 0,結果會得到是 Infinity,而任何負數除以 0 會得到 -Infinity

咦? 你問 0 / 0 嗎? 結果會得到 NaN
甚至是 Infinity / Infinity-Infinity / -Infinity 同樣地也會得到 NaN

NaN 在 JavaScript 當中是個有趣的存在,就字面上來說,它不是個數字,但你若用 typeof 運算子來判斷型態,它又會告訴你這是個 number

typeof(NaN);    // "number"

NaN 與任何數字作數學運算,結果都是 NaN。 也就是說, NaN 並不等於任何的數字,甚至是自己。

NaN === NaN;    // false

https://ithelp.ithome.com.tw/upload/images/20171206/200655045JowgKH5AP.jpg

既然這樣,那要怎麼檢查一個變數是否為 NaN 呢? 這個時候可以透過 isNaN(value) 來幫助我們判斷。

isNaN(NaN);       // true

isNaN(123);       // false
isNaN("123");     // false, 因為字串 "123" 可以透過隱含的 Number() 轉型成數字

isNaN("NaN");     // true, 因為字串 "NaN" 無法轉成數字

值得一提的是,跟多數其他程式語言一樣的問題是,JavaScript 的 number 實作是基於「IEEE 754」二進位浮點數算術標準 [註2],所以當你執行 0.1 + 0.2 === 0.3 的時候,你會得到 false 的結果。

這不是瀏覽器的問題,因為十進位的小數無法完美的用二進位的方式表示,只能用無限循環的位數來趨近於十進位的小數,若以 IEEE 754 規定的 24 位數為上限時,在儲存時就會省略一些位數,導致還原時的小數不夠精準。

0.1 以二進位表示會是 0.0001100110011001100110011001100110011001100110011001100... (無限循環)

0.2 以二進位表示會是 0.0011001100110011001100110011001100110011001100110011010

兩者相加後得到 0.0100110011001100110011001100110011001100110011001100111

再轉回十進位剛好就變成 0.30000000000000004

https://ithelp.ithome.com.tw/upload/images/20171206/20065504ueFAZF45r9.jpg

如果要求數字精準的話,簡單的處理方式可將小數點在運算前轉成整數,計算後再調整回來,但遇到大數會有例外或爆掉的問題。 目前看到最佳的解法應該是這個 number-precision,有興趣的朋友可以作為參考。


boolean 布林值

與其他類別相比,boolean 就顯得單純地多,boolean 的名字由發明的科學家 George Boole 命名,其中的值只有兩種:true 以及 false

布林值通常用在判斷式,作為控制程式流程的用途。

var a = true;
var b = false;

var c = ( 100 > 10 );    // true

在 JavaScript 判斷比較的運算式中,所有東西都可以隱含轉換為布林值,這個部分留待下篇型別轉換時再詳細介紹。


null 與 undefined

在大多數的程式語言中,都有 null 或是類似空值類型的設定,而 JavaScript 又多了一個 undefined,這裡就把它們放在一起比較。

這兩個類型的共通點是,null 型別只有一種值,就是 null ,而 undefined 類型也只有一種值,就是 undefined

var a;         // undefined, 尚未給值,未定義

var b = null;  // null, 明確代表此變數沒有值

其實可以看出,當變數 a 被宣告時,在沒有給變數任何數值的情況下,變數的預設值會是 undefined,而變數 b 則是直接被明確地設定為 null

雖然這兩種值透過 Boolean() 強制轉型成 boolean 時,都會代表 false 的意思,但兩者間仍然有意義上的差別。

  • undefined 代表的是「(此變數) 還沒有給值,所以不知道是什麼」
  • null 代表的是「(此變數可能曾經有值,可能沒有值) 現在沒有值」

我想這點在透過 Number() 強制為兩者轉型的時候多少可以看出點什麼:

Number( null );         // 0

Number( undefined );    // NaN

除此之外,另外一個 有趣 討人厭的地方是,在非全域作用範圍下, undefined 允許被當成變數名稱,而且變數的值是可以被修改的:

(function() {
  var undefined = 'foo';
  console.log(undefined, typeof undefined);    // 'foo', 'string'
})()

甚至是作為參數使用:

(function(undefined) {
  console.log(undefined, typeof undefined);    // 'foo', 'string'
})('foo');

雖然這樣都是合法的,但為了身心健康,請不要惡搞你的程式碼。
但是同事的可以 (大誤)

註1: ES6 之後多了新的型別:Symbol。
註2: IEEE 754 是 「IEEE 二進位浮點數算術標準」,有興趣的朋友可至維基百科 IEEE 754 查閱。


以上是今天針對變數與資料型別(基本型別)的介紹,在後續文章中,會繼續介紹「物件型別」以及型別之間的自動/指定轉換,歡迎有興趣的朋友繼續關注。


上一篇
重新認識 JavaScript: Day 02 JavaScript 簡介
下一篇
重新認識 JavaScript: Day 04 物件、陣列以及型別判斷
系列文
重新認識 JavaScript37

尚未有邦友留言

立即登入留言