2012/02/08

JavaScriptにおける数値と文字列の相互変換

JavaScriptにおける(変な)数値と文字列の相互変換の方法のまとめ。
使い古されたものばかりなので、かなり既出。
基本の方法だけ使ったほうが、安全かつ美しいコードが書けるのでトリビア扱いで。

数値→整数編

ビット演算は符号付き32bit整数として扱われることを利用したテクニック。
一歩間違うと小数の誤差やらオーバーフローにはまってしまう、諸刃の刃。
//基本
Math.floor(n);
//-1とAND
n&-1;
-1&n;
//0とOR
n|0;
0|n;
//0とXOR
n^0;
0^n;
//0回シフト
n>>0;
n<<0;
//2回補数を取る(=もとに戻るので整数化の効果のみ残る)
~~n;

文字列→数値編

算術演算を行い、演算子の前後の項が数値であるという仮定から数値化するテクニック。
//基本
Number(str);
parseFloat(str);
//0を引く
str - 0;
//足すと文字列の連結となってしまうためできない!
0 + str;//0 + "1" == "01"
str + 0;//"1" + 0 == "10"
//1で割る
str / 1;
//1をかける
1 * str;
str * 1;
//正の数とみなす(これの理由は後述)
+str;
//無限大の剰余(割った余り)。n/無限大の商は0で、余りがnになることを利用
//str / 1のほうが手っ取り早いのであんまり意味はない
str % Number.POSITIVE_INFINITY;
str % (1 / 0);//0で割るとInfinityになるため
+strが出来る理由は、ECMA-262 3rdの11.4.6(単項の+は項を数値化する)があるから。
感覚的には"単項の-nがn*-1を行う"ので"単項の+nはn*1を行う"と考えるとわかりやすい。
つまり、
//2回負の数とみなす(2回符号を反転。つまりstr*-1*-1 = str*1)
-(-str);
こんな無駄な方法も一応可能。
ただし、--strのように書くとデクリメントになるので注意。

また、文字列を整数にしたい時は以下のような方法も可能。
//基本
parseInt(str, 10);
//-1とAND
str&-1;
-1&str;
//0とOR
str|0;
0|str;
//0とXOR
str^0;
0^str;
//0回シフト
str>>0;
str<<0;
//2回補数を取る
~~str;

数値→文字列編

数値化に算術演算子を用いたときの逆の考えで可能。
文字列を要求する項は文字列として扱われることを利用する。
//基本
String(n);
//十進数化(10は省略可能)
(n).toString(10);
//限りなく存在感の薄い子その1(引数省略でn.toString()と同等)
(n).toPrecision();
//限りなく存在感の薄い子その2(引数省略で小数第一を四捨五入した文字列になる)
(n).toFixed();
//""(長さ0の文字列)と連結する("01"+2が"012"になる原理)
"" + n;
n + "";

どうでもいいおまけ

小数第一位で四捨五入してみる。
//基本
Math.round(1.5);//2
Math.round(1.4);//1
//四捨五入になる仕様らしい(でも全有効桁数指定なので汎用性がない)
(1.5).toPrecision(1);
(1.4).toPrecision(1);
//同じく四捨五入(こっちは小数点以下の有効桁数指定なので汎用性がある)
(1.5).toFixed();
(1.4).toFixed();
//古典的な手法(0.5足してから整数化。数値→整数編の他の方法も可)
(1.5 + 0.5)|0;
(1.4 + 0.5)|0;

まとめ

いろいろとカオスな方法で文字列と数値は相互に変換できる。 主な利点は以下の2つ。
  • コードが短くなる
  • 高速に動作する
コードを短くできると、ショートコーディングにはかなり便利(というか、むしろ必須)。
高速であることはおそらく、プロパティへのアクセス(ドット演算子は遅い)や関数を呼ぶコストなどなどが絡んでくるため。
数値演算まわり(Math.floorなど)はビット演算がかなり効いてくるので、時間的にシビアな場合は活用できるかと。

このあたりの検証は適当に検索すればベンチマークがたくさんあると思うので割愛。
こういうちょっとした部分でのパフォーマンスの違いは、JSエンジンの進化が何とかしてくれることを期待してるんだけど、意外にしぶとい……。