DanLevy.net

クイズ:JavaScript、ちゃんと数えられますか?

` parseIntparseFloat、区別つきますか? `

parseInt(" 123456.00")

parseInt 関数はスペースを無視し、先頭の数字の列を整数としてパースします。ここでは小数点の位置で処理が停止するため、123456 のみが返されます。

parseInt("123,456.00")

原則として、parseInt は数値以外の文字に遭遇すると解析を停止します。このケースではカンマがそのトリガーとなるため、結果として 123 のみが返されます。

0.1 + 0.2 === 0.3

浮動小数点数の精度エラーにより、0.1 + 0.2 は厳密には 0.3 と等しくなりません。メモリ内での浮動小数点数の格納方式により、実際の計算結果は 0.30000000000000004 になります。浮動小数点演算を規定する IEEE 754 標準が原因で、特定の数を完全に正確に表現できないのです。これはあらゆるプログラミング言語に共通する課題です。無限循環小数にぶつかった時点で、どの言語を使おうとコンピュータは無限に続く桁の計算をどこかで打ち切らざるを得ません。

Python や Java などの言語にはこの問題を回避するための DecimalBigDecimal が標準で備わっていますが、JavaScript には組み込まれていません。こうしたケースを扱うには、big.jsdecimal.js といったライブラリの導入が推奨されます。

(注:分数や虚数などをより高次の論理レベルで扱い、リテラル式をそのまま保持するよう設計された言語も存在します。しかし、ハードウェアレベルでは依然として同じ浮動小数点数の精度問題と向き合わなければなりません。)

Number.MAX_VALUE * 2

Number.MAX_VALUEはJavaScriptで表現可能な最大の標準の数値です。この限界を超えるとすぐにオーバーフローし、実質的に意味のない値が返されます。これに2を掛けると、結果はInfinityになります。

まあ、JavaScriptって時々こうなるんですよね。

このコードを実行するとどうなる?

5..toFixed(2)

.toFixed(2)5 を小数点以下2桁の文字列として返すため、結果は "5.00" になります。

ドットが2つ続く記法(5..toFixed(2))は、数値リテラルのオブジェクトモデルにアクセスするためのちょっとした「トリック」です。

parseInt("42") === parseFloat("42")

JavaScriptにおいて、parseIntparseFloat はどちらも文字列 "42" を数値 42 として解釈します。そのため、parseInt("42") === parseFloat("42") の比較結果は true になります。parseInt は最初の非数字文字で解析を停止しますが、parseFloat は浮動小数点数の一部ではない文字に遭遇するまで解析を続けます。ただし、"42" には小数点やその他の非数値文字が含まれていないため、両関数は同じ値を返します。

BigInt("42") === parseInt("42")

BigIntnumber とは異なるプリミティブ型です。そのため、parseInt("42") が返す通常の数値は、BigInt("42") と厳密等価(===)にはなりません。値を正しく比較するには、両方を同じ型に揃える必要があります。例えば BigInt(parseInt("42")) === BigInt("42") のように明示的に変換してください。

このコードの実行結果はどれだ?

parseInt("0x2A") === parseInt("2a", 16)

先頭が 0x で始まる文字列は、自動的に16進数(基数 16)として扱われます。 つまり、基数に16を明示的に渡すのと同じ動作になります。したがって、parseInt("0x2A")parseInt("2a", 16) と完全に同等です。(大文字・小文字は区別されません。)

さて、これはどうなる?

parseInt('0xFF', 16)

parseInt に16進数(16)の基数を指定すると、"FF" が10進数の 255 に変換されます。CSSのRGB/16進数カラーコードで見たことがある人も多いはずです。

[24, 'One', 42].map(parseInt)

parseInt の第2引数(基数)は、配列メソッドのコールバックに渡される index 引数と勝手に紐付いてしまいます。これが予期せぬ結果を招く原因です。例えば parseInt("One", 1) は基数1が不正なため NaN を返します。

[24, 'Twenty1', 0o42].map(Number)

NumberparseInt よりも厳密に値を数値型に変換します。ここでは、'Twenty1'NaN になり、0o42 は8進数リテラルとして認識されて 34 に変換されます。

このコードの実行結果は?

console.log(parseInt(null), Number(null))

parseInt は入力を文字列に強制変換するため、null"null" になります。"null" には有効な10進数の文字(通常の数字)が含まれていないため、NaN が返されます。

Number(null)0 を返します。JSは開発者を油断させないのが好きなんですよね。 なぜそうなるのか?興味があれば、もっと深く掘り下げるかもしれません。

この呪文の実行結果は?

parseInt(null, 36)

parseInt は常に引数を文字列に強制変換するため、null は文字列 "null" になります。

36進数(hexatrigesimal、覚えてますか?)では、文字列 "null"1112745 を表します。

36進数において、nulknullnulm の連続する値はそれぞれ 111274411127451112746 です。

比較表

関数parseIntparseFloatNumberBigInt
空白文字を無視
.map(FN)☑️
基数(radix)引数をサポート
2進数/8進数/16進数リテラル
無効な文字を含む 42 oh no4242NaNSyntaxError

手応えはどう? 🧐

2進数攻めで一息つきたい?
ふっ、覚えておけ:スキルを身につけた に休め!

もっと課題を叩きのめしたいなら、俺のジム へ!💪