本系列文章已重新編修,並在加入部分 ES6 新篇章後集結成書,有興趣的朋友可至天瓏書局選購,感謝大家支持。
購書連結 https://www.tenlong.com.tw/products/9789864344130
讓我們再次重新認識 JavaScript!
上一篇文章我們簡單介紹了 JavaScript 的歷史,那麼從這篇開始將進入語法基礎篇。 JavaScript 的語法其實大量借鑒了 C 語言以及其他類似 C 語言 (如 Java) 的語法,所以過去如果你也寫過這種類語言的話,相信可以很快上手。
今天要介紹的主題就是 JavaScript 的變數與資料型別。
變數是用來儲存資料和進行運算的基本單位,可以將變數想像為一個盒子,用來存放資料。
在 JavaScript 中的變數宣告有其一定的規則,變數的第一個字母必須為英文字母、底線 _
或是錢字號 $
,後面可以是英文字母、底線 _
或是錢字號 $
以及數字。 變數名稱不可以是保留字 (Reserved Words) 與關鍵字 (keyword) [註1]。
需要注意的是,JavaScript 的語法是有區分大小寫的,也就是說,變數 app
與 App
在 JavaScript 會被認為是兩個不同的變數,而且自 JavaScript 1.3 之後開始支援 Unicode。
換句話說,你的變數名稱是可以用中文命名的,而且完全合法,但基於開發習慣還是盡量避免使用非英文字母、底線或是 $
以外的字元來命名變數,用中文編碼的話,其他語言的系統開啟可能會變亂碼。
在 ES6 以前,變數在使用前,可以透過 var
這個關鍵字來宣告。
在 ES6 之後宣告「變數」與「常數」,除了 var
之外,還可以透過 let
與 const
做宣告,這部分我們之後講到 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);
像這樣
但在開發的時候你可能會發現一件事,就是即便你的變數在事前沒有透過 var
做宣告的時候,仍然可以給定變數初始值,但強烈不建議這麼做。
要記得一句話,所有沒有透過 var
宣告的變數都會自動變成全域變數。
什麼是「全域變數」? 之後在介紹變數的作用範圍會再詳細解說。
// 對未宣告的變數 m 賦值
m = 1;
console.log(m); // ok 好,會出現 1
註1: 關鍵字指的是 ECMAScript 所規定具有特定用途的英文單字,不能用來作為變數名稱使用。 而保留字則是雖然目前在 JavaScript 還沒有特殊用途,但在未來有可能會被拿來當關鍵字來使用,所以也不能作為變數名稱。 請參考 MDN 關鍵字與保留字列表。
註2: 在程式語言中,會依照語言的型別系統 (Type system) 來分成「強型別語言」與「弱型別語言」兩種。 強型別指的是「程式所定義的變數型別等於變數在執行時期的型別」,也就是說,這類語言的變數在被宣告的時候,必須指定一種資料型別給它,如果對這個變數做了錯誤型別的運算時,就會出現錯誤,也可以利用編譯器提前做型別檢查,就可以減少在執行時期 (Runtime) 發生的錯誤。 弱型別語言則正好相反,雖然取得了語法簡潔的優點,但要注意型態轉換時產生非預期的問題。
剛剛說過,由於 JavaScript 是個「弱型別」的程式語言,嚴格來說,變數本身其實不帶有資料型別的資訊,其中的「值」或「物件」才有。 所以說在執行時期透過變數內容來參考至物件或值,才會得知此變數有什麼操作的方法。
變數沒有型別,值才有。
在 ES5.1,JavaScript 內建的型別主要可以分成基本型別 (Primitives) 與物件型別 (Object) 兩大類。
而基本型別又分成 string
、number
、boolean
、null
、undefined
幾種 [註1],除了上述幾種之外,都可以歸類至物件型別 (Object)。
判斷型別的方式,可以透過 typeof
運算子來協助我們,但有時會有例外需要處理。
typeof true; // 'boolean'
typeof 'Kuro'; // 'string'
typeof 123; // 'number'
typeof { }; // 'object'
typeof [ ]; // 'object'
// 下面兩個要特別注意,之後的篇幅會介紹到
typeof window.alert; // 'function'
typeof null; // 'object'
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 = '這不是一行文 \
這是第二行 \
這是第三行';
要注意的是 \
反斜線符號後面不能有任何東西,包括空白字元。
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
既然這樣,那要怎麼檢查一個變數是否為 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
如果要求數字精準的話,簡單的處理方式可將小數點在運算前轉成整數,計算後再調整回來,但遇到大數會有例外或爆掉的問題。 目前看到最佳的解法應該是這個 number-precision,有興趣的朋友可以作為參考。
與其他類別相比,boolean
就顯得單純地多,boolean
的名字由發明的科學家 George Boole 命名,其中的值只有兩種:true
以及 false
。
布林值通常用在判斷式,作為控制程式流程的用途。
var a = true;
var b = false;
var c = ( 100 > 10 ); // true
在 JavaScript 判斷比較的運算式中,所有東西都可以隱含轉換為布林值,這個部分留待下篇型別轉換時再詳細介紹。
在大多數的程式語言中,都有 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 查閱。
以上是今天針對變數與資料型別(基本型別)的介紹,在後續文章中,會繼續介紹「物件型別」以及型別之間的自動/指定轉換,歡迎有興趣的朋友繼續關注。