增強的數值與字串
July 31, 2022JavaScript 中的基本資料型態包括了數值、字串與布林值,型態名稱分別為 number
、string
與布林值 boolean
,在 ES6 以後,多了個 symbol
,ES11 以後,多了 bigint
,這會在之後說明。
數值
至於 number
的部份,ES6 以後可以使用 Number.MAX_SAFE_INTEGER
與 Number.MIN_SAFE_INTEGER
來取得可表達的最大整數與最小整數,而 Number.isSafeInteger
可用來檢查一個整數是否在安全範圍內:
> Number.MAX_SAFE_INTEGER;
9007199254740991
> Number.MIN_SAFE_INTEGER;
-9007199254740991
> Number.isSafeInteger(9007199254740992);
false
> Number.isSafeInteger(9007199254740991);
true
> Number.isInteger(9007199254740992)
true
>
Number.isInteger
只檢查數字是不是整數,不會考慮它是不是安全範圍內的整數。至於兩個符點數之間,最小的差距,可以使用 Number.EPSILON
來取得:
> Number.EPSILON;
2.220446049250313e-16
>
可以使用 0b
(或 0B
) 撰寫二進位數來表示數字,或者是 0o
(或0O
?別這麼寫啦!)撰寫八進位數來表示數字。例如:
> 0b0100
4
> 0o77
63
>
(在 ES5 嚴格模式下,不允許使用八進位 077
來表示 63,因為有人會誤用 0 來對齊數字。)
如果你有個字串 '0b0100'
、'0o77'
,可以使用 Number
(而不是使用 parseInt
)來轉換為 10 進位數字,例如:
> Number('0b0100')
4
> Number('0o77')
63
>
只不過,這就與 Number
的 toString
字串不一致了,因為它少了 0b 或 0o 的前置:
> (4).toString(2)
'100'
> (63).toString(8)
'77'
> parseInt('100', 2)
4
> parseInt('77', 8)
63
>
Number
的 toString()
,還是得配合 parseInt
,不過,ES6 以後的 Number
上有個 parseInt
也有個 parseFloat()
,與全域的 parseInt
與 parseFloat
是相同的:
> Number.parseInt === parseInt
true
> Number.parseFloat === parseFloat
true
>
因為 ES6 以後想要遠離全域的束縛,一些原本是全域函式的東西,都被放到了某個名稱之下,不過,有些獲得了增強,例如 Number.isNaN
與 isNaN
行為上是不同的,只有 NaN
能讓 Number.isNaN
為 true
了:
> Number.isNaN(NaN);
true
> Number.isNaN(1 / 'two');
true
> Number.isNaN('caterpillar');
false
> Number.isNaN(undefined);
false
> Number.isNaN('0');
false
>
Number.isFinite
與 isFinite
也不同,Number.isFinite
遇到非 number
就會是 false
:
> isFinite('15')
true
> Number.isFinite('15')
false
> Number.isFinite(15);
true
> Number.isFinite(Infinity);
false
>
字串
在字串的部份,ES6 以後如果字串中的字元不有 0000 ~ FFFF 的範圍之中(也就是 BMP 外的字元),例如高音譜記號的 1D11E,可以直接使用 \u{1D11E}
來表示了,使用 '\uD834\uDD1E'
也是可以啦!
> '\u{1D11E}' === '\uD834\uDD1E';
true
>
ES6 以後支援模版字串(Template string),必須使用 ` 來建立模版字串,不少文件最愛舉的例子是它可以換行,例如,有個 helloworld.js 檔案寫了以下的內容的話:
let html = `<!DOCTYPE html>
<html>
<head>
<title>Hello, World</title>
</head>
<body>
Hello, World
</body>
</html>`;
console.log(html);
執行這個 .js 檔案你會看到:
C:\workspace>node --use_strict helloworld.js
<!DOCTYPE html>
<html>
<head>
<title>Hello, World</title>
</head>
<body>
Hello, World
</body>
</html>
該換行的都換行了,簡單來說,它會保留 ` 之間的內容。例如:
> let s = `Your left brain has nothing right.
... Your right brain has nothing left.`
undefined
> s;
'Your left brain has nothing right.\nYour right brain has nothing left.'
> typeof s;
'string'
>
一個模版字串的型態也是 string
。模版字串中若 ${}
的部份,會執行 ${}
中的內容,然後取得結果再與其他字串結合在一起,例如:
> `1 + 2 = ${1 + 2}`
'1 + 2 = 3'
> let a = 1;
undefined
> let b = 2;
undefined
> `${a} + ${b} == ${a + b}`
'1 + 2 == 3'
> let o = {p : 10};
undefined
> `${o.p}`;
'10'
> let arr = [1, 2, 3];
undefined
> `Double arr: ${arr.map(n => n * 2)}`;
'Double arr: 2,4,6'
>
最後一個看到的是 ES6 以後支援的箭號函式(Arrow function),之後就會談到。可以想見的,如果想要建立模版,例如 HTML,使用模版字串很方便,例如:
> let title = 'Hello, World';
undefined
> let message = 'Hello? World?';
undefined
> let html = `<!DOCTYPE html>
... <html>
... <head>
... <title>${title}</title>
... </head>
... <body>
... ${message}
... </body>
... </html>`;
undefined
> console.log(html);
<!DOCTYPE html>
<html>
<head>
<title>Hello, World</title>
</head>
<body>
Hello? World?
</body>
</html>
undefined
>
標記模版
經常被拿來在說明模版字串之後提及的是標記模版(Tagged template),如前面提過的,模版字串中若 ${}
的部份,會執行 ${}
中的內容,然後取得結果再與其他字串結合在一起,如果你想分別處理其他字串以及 ${}
運算結果,例如 ${}
可能來自使用者輸入,而你想要將有安全疑慮的字元替換掉,那標記模版(Tagged template)就會派上用場。
不過,標記模版其實是個函式呼叫的特殊形式,如果你有個函式 f
,那麼:
let a = 10;
let b = 20;
f(`${a} + ${b} = ${a + b}`);
結果就相當於:
f('10 + 20 = 30');
然而,如果你使用:
f`${a} + ${b} = ${a + b}`
就會將 ${}
外的字串分割出來,使用陣列儲存,然後運算出 ${a}
、${b}
、${a + b}
的值,最後再用陣列與運算出來的值來呼叫函式,也就是相當於:
f(['', ' + ', ' = ', ''], 10, 20, 30);
接下來,就看你的函式中怎麼處理這些值了,例如:
function f(strings, value1, value2, value3) {
// 函式處理
}
如果你的標記模版中 ${}
數量是固定的,這樣就夠了,不過,若是事先無法決定個數,那麼可以定義以下這樣的函式:
function f(strings, ...values) {
// 函式處理
}
...
是 ES6 以後的 Rest 運算子,當它出現在參數時,會將呼叫函式時其餘的引數收集在一個陣列中,當成函式的引數傳入,因此就標記模版來說,就是將 ${}
的結果收集至陣列,然後當成第二個引數傳入函式了。
如果你想要 console.log
能顯示 '\n'
字樣,基本上在字串中要 escape:
> console.log('ABC\nEFG');
ABC
EFG
undefined
> console.log('ABC\\nEFG');
ABC\nEFG
undefined
> console.log(`ABC\nEFG`);
ABC
EFG
undefined
> console.log(`ABC\\nEFG`);
ABC\nEFG
undefined
>
ES6 以後有個 String.raw
函式,搭配標記模版,可以直接 escape,讓 \n
這類的字元,不會被轉譯為換行:
> String.raw`ABC\nEFG`
'ABC\\nEFG'
> console.log(String.raw`ABC\nEFG`)
ABC\nEFG
undefined
>
函式搭配標記模版時,函式的第一個參數值雖是陣列,然而會加上一個 raw
特性,可用來取得未轉譯字串的清單:
> function f(strings) {
... console.log(strings);
... console.log(strings.raw);
... }
undefined
> f`ABC\nEFG`
[ 'ABC\nEFG' ]
[ 'ABC\\nEFG' ]
undefined
>