JavaScript の算術演算子と数値変換

valueOf と ToNumber とあとついでに parseInt のことについて適当に調べてそれを垂れ流したのでまとめてみた。 調べてなくてあまり確証のないところは”(たぶん”とかつけているけれど、それ以外は一応調べたつもりなのでだいたい合ってるハズ。 但しブラウザ対応までは調べていないので、悪しからず。
4
(っ=﹏=c) .。o○ @itchyny

var t = +new Dateって, これなんで動くんだっけか

2010-12-20 21:37:09
(っ=﹏=c) .。o○ @itchyny

+付けたらvalueOfが返されるのか, 巧妙だ http://goo.gl/NYIem

2010-12-20 21:46:04
(っ=﹏=c) .。o○ @itchyny

どうだ, 混乱してきただろう! ハッハッハッ http://twitpic.com/3hqyck

2010-12-20 22:07:33
拡大
Tomoki UDA @t_uda

いちにぃさんのついーとを見て String の valueOf 絡みのことを調べているが、これは思った以上にややこしい。そして思わぬ収穫が。 /[Mm]ath/

2010-12-20 22:55:17
Tomoki UDA @t_uda

. #JavaScript (valueOf について)話をしよう・・・ #JavaScript /[Mm]ath/

2010-12-20 23:13:46
Tomoki UDA @t_uda

結論から言うと、算術演算子は、内部で被演算項に対して数値変換を施してから演算を行うようになっている。従って、次のようなコードは結果が数値型になる(ことが期待される): "1" - "2"; // => -1 #JavaScript /[Mm]ath/

2010-12-20 23:17:34
Tomoki UDA @t_uda

確認は Firefox, Chrome でとった。そしてこれは ECMAScript 仕様に則った挙動である。 #JavaScript /[Mm]ath/

2010-12-20 23:59:25
Tomoki UDA @t_uda

ここで、数値型への変換が呼ばれる前に、 valueOf が呼ばれる場合がある。(なお ECMAScript の仕様ではそれぞれ "ToNumber", "GetValue" となっている) #JavaScript /[Mm]ath/

2010-12-21 00:24:42
Tomoki UDA @t_uda

呼ばれる場合がある、というのは必ずしも valueOf は呼ばれないということである。実は valueOf と GetValue は等価ではない。 #JavaScript /[Mm]ath/

2010-12-21 00:28:15
Tomoki UDA @t_uda

仕様にある "GetValue" というのは、あくまで「ECMAScript のオブジェクトから primitive 値を取得する」という意味である。 #JavaScript /[Mm]ath/

2010-12-21 00:30:41
Tomoki UDA @t_uda

そして Object は全てのオブジェクトの継承元であるから、 ***.prototype.valueOf は、Number や String にも(nativeに)実装されている。 #JavaScript /[Mm]ath/

2010-12-21 00:33:14
Tomoki UDA @t_uda

従って JavaScript のオブジェクトなら全て GetValue の時に valueOf が呼ばれていることになる(そしてこの呼び出しは時に script レベルで再帰的でもあるかもしれない)。 #JavaScript /[Mm]ath/

2010-12-21 00:35:24
Tomoki UDA @t_uda

ここで重要なのが primitive 値だ。 primitive な string や number は普通のオブジェクトとは全く異なる。これは”GetValue が返すべき値そのもの”である(たぶん。 #JavaScript /[Mm]ath/

2010-12-21 00:37:20
Tomoki UDA @t_uda

数値リテラル初期化子や、文字列リテラル初期化子が生成する値は、いずれも primitive 値である。それぞれ、Number, String の instance で は な い。 #JavaScript /[Mm]ath/

2010-12-21 00:38:21
Tomoki UDA @t_uda

例えば +3 というコード片において、3 は primitive 値 [3] を生成するから、 [GetValue] によってその値自身 [3] を返す。ここで valueOf が呼び出される余地はない(ハズである。 #JavaScript /[Mm]ath/

2010-12-21 00:42:57
Tomoki UDA @t_uda

((ここら辺まで ECMAScript とか valueOf とかの実装寄りの話 / こっからついでに分かった話)) /[Mm]ath/

2010-12-21 00:56:43
Tomoki UDA @t_uda

JavaScript で文字列->数値に変換するときは、よく parseInt 関数が使われる。(parseInt を用いるのが最も一般的だと思われる) #JavaScript /[Mm]ath/

2010-12-21 00:59:22
Tomoki UDA @t_uda

(と言うか、他に知らない。ECMAScript レベルで何か規定されていれば流石に知ってるハズ。) /[Mm]ath/

2010-12-21 00:59:46
Tomoki UDA @t_uda

ところが、parseInt には色々と問題がある。ブラウザによって必ずしも同じ値を返さなかったりするのはまぁいつものことだが、引数の与え方によっては 8 進数表記だと解釈されたりしてしまう。 #JavaScript /[Mm]ath/

2010-12-21 01:02:21
Tomoki UDA @t_uda

例えば、 parseInt("010") は基数を指定していないため、多くのブラウザで 8 が返ってくる。ちなみに IE9 では 10 が返ってきた。 #JavaScript /[Mm]ath/

2010-12-21 01:07:05
Tomoki UDA @t_uda

ECMAScript draft 5th ed. では、第二引数省略時は必ず 10 進表記で変換することになったらしいので、IE9 の挙動が正しい。 #JavaScript /[Mm]ath/

2010-12-21 01:08:28
Tomoki UDA @t_uda

と言ってもそんなのは最近の出来事である。昔の draft では実装依存だったらしいので、parseInt の第二引数を指定しない場合の結果はあまり期待できない。(なので、第二引数には必ず 10 をつけろ、と言われている) #JavaScript /[Mm]ath/

2010-12-21 01:09:50
Tomoki UDA @t_uda

また、parseInt は科学表記(2×10^-5 を表す 2.0e-5 など)に対応していない。 parse 中に、期待されない文字が現れた場合以降の文字は切り捨てられる。 #JavaScript /[Mm]ath/

2010-12-21 01:12:18
Tomoki UDA @t_uda

例えば: parseInt("2e-3"); // => 2 parseInt("1abcd"); // => 1 #JavaScript /[Mm]ath/

2010-12-21 01:13:15