2013年6月2日日曜日

Kuriboさん( @kurikuribo )のブログ(クリボウのプログラミングひとりごと)の以下の投稿を読んで。

JavaScript の parseInt ではまる

parseInt は "010" で 8 進数、"0x10" で 16 進数として解釈されたりする

試したみたら、parseIntの挙動がクリボウさんの記事とちょっと違ったり、ややこしかったので書いてみた。

コード(BBEdit)

var result = "",
  num_strs = ["010", "0x10", "0xf", "08", "09", "0xg",
                "1234567890", "12a3b4c5d6789"],
    i, max;
result += "それぞれの「文字列」をparseIntの第1引数に渡した結果(基数は省略)\n";
for ( i = 0, max = num_strs.length; i < max; i += 1 ) {
    result += num_strs[i] + ": " + parseInt(num_strs[i]) + "\n";
}
$('#pre0').text(result);




結果を確認したのはMacのそれぞれ最新版のSafari、Firefox、Chrome。WindowsやIE等では確認してません。(>_<)

結果をみると、8進数(010)の文字列を渡したとき結果は10となる。(ここが、クリボウさんの記事と挙動が違った箇所。"010"をparseIntに渡しても10進数として解析された。) 基数を省略した場合、10進数として解析されるから。また、無効な8進数08、09を文字列として渡した場合も同様に10進数として解析されるから、結果はそれぞれ8、9となる。(8進数で有効な数値は0から7まで。)

第2引数の基数を省略して、さらに第1引数に渡した文字列が0xか0Xで始まる場合、16進数として解析される。出力結果から、確かに0x10は16、0xfは15となる。16進数では無効な数値(0xg)を文字列で渡した場合、結果はNaNになる。(16進数で有効なのは、0から9までの数値、aからf(大文字小文字は関係ない)のアルファベット)

ここでややこしいと感じたのは、第2引数の基数を省略した場合、引数に渡した文字列が、0から始まる場合は8進数で解析されずに10進数として解析されるのに、0x、あるいは0Xから始まる場合は16進数として解析されること。すなわち、第2引数デフォルト値が常に10であるわけではないこと。(10じゃなくなるのは0xか0Xの場合だけではあるけど。)

ちなみに、JavaScript: The Good Parts -「良いパーツ」によるベストプラクティス( Douglas Crockford (著) 水野 貴明 (翻訳)オライリー・ジャパン、2008年、ISBN978-4-84311-391-3)のp.120、付録 A ひどいパーツ A.7 parseIntに、

最初の文字が「0」の場合、文字列は10進数ではなく8進数であると見なされる。8進数では8と9は数字とは見なされない。したがってparseInt("08")とparseInt("90")の結果は0になってしまう。

とあったけど、実際には8と9になった。仕様が変わったのかなぁ〜(以前は、上記のややこしいと感じた、0xの場合は16進数として解析されるのと同様に、0から始まる文字列の場合は8進数として解析されてたのかなぁ。8進数と16進数が10進数として解析されないのが、今では16進数だけが10進数として解析されなくなったなら、特殊な場合が2つから1つになったのは良くなったことかもしれないけど、なんで16進数だけ残したのか気になる。。)

ということで、常に基数をパラメーターに渡すようにすれば、ややこしかったり誤解したり、勘違いするようなこともなくなるので、そういう習慣にするのは良いことかも。

コード(BBEdit)

var result = "",
  num_strs = ["010", "0x10", "0xf", "08", "09", "0xg",
                "1234567890", "12a3b4c5d6789"],
    radixes = [8, 10, 16],
    i, max, j, max_j;
result += "それぞれの「文字列」をparseIntの第1引数に渡した結果(基数は8と10と16)\n";
for ( i = 0, max = num_strs.length; i < max; i += 1 ) {
    for ( j = 0, max_j = radixes.length; j < max_j; j += 1) {
        result += "parseInt('" + num_strs[i] + "', " + radixes[j] + "): " +
                      parseInt(num_strs[i], radixes[j]) + "\n";
    }
}
$('#pre1').text(result);










						

1 コメント :

Kuriboさんのコメント...

parseInt("010") は、IE 8 の場合 8 に、IE 9 で 10 になりました。新しいブラウザで仕様が変わったんでしょうね。
ともかく、parseInt("010", 10) としておくのが無難と思った次第です。

コメントを投稿