JavaScriptCoreでparseInt('010')が十進数として処理されるように(→V8, SpiderMonkeyも追従)

ちょっと前に、JavaScriptCoreparseInt()の挙動が変更されて、0から始まる文字列を8進数として認識しなくなった。

parseIntは入る文字列によって、数値の底が変わっちゃう。基本的には十進数として処理するけれど、"0x10"みたく0x(もしくは0X)から始まる文字列が渡された場合、文字列は16進数として解釈され、処理される。だから、parseInt('0x10')16を返す。

ここまではまだいいとして、"010"とか0から始まる数値(の文字列)が入ると、それを8進数として処理してしまう「ことがある」。ES3のparseIntの項にはこんな注釈がある。

When radix is 0 or undefined and the string's number begins with a 0 digit not followed by an x or X, then the implementation may, at its discretion, interpret the number either as being octal or as being decimal. Implementations are encouraged to interpret numbers in this case as being decimal

最初に0であっても十進数と解釈するのが望ましいとは書かれてるんだけど、8進数として処理されるブラウザが多かった。

これ、十進数を必ず返すものと思っていて、単にparseInt('str')と使ってる人、結構いるんじゃないかなあと思う。前に何かでバグの原因を探ってて、これだったことがあった。

対処するのは簡単。parseIntは第二引数をとれて、それで底を指定できる。十進数を意図してるなら常にparseInt('010', 10)としとけばいい。

さて、ES5ではこの妙な注釈が削除されて、8進数としての処理はしちゃいけないことになった。

15.1.2.2: The specification of the function parseInt no longer allows implementations to treat Strings beginning with a 0 character as octal values.

ただ、既存コンテンツとの互換性かなんなのか、ES5の仕様が固まったあとも対応が遅い。最初に対応したのがIE9で、次はJavaScriptCoreになる。
OperaES5.1フルサポートとうたってるけど、テストした限り動いてくれない。あと、詳細なサポート表を見たらOpera Presto partially supports ECMAScript 5ってなってる。えええ……

Geckoに関しては、一度対応したのだけれど、やめた経緯がある。

Jeff Waldenのコメントで、Strict modeのみで対応したけれど、strictnessで判別して挙動を変えることの問題などからバックアウトされた経緯が語られている。

他のブラウザも同じ挙動だから、それを直さず意図的に対応しないと。
Chromeで使われてるV8も同様に対応しない方針らしい。

ただ、IE9が変わったのはシェア的にも大きいし、JSCもそれに続くとすると、SafariChromeで挙動が違うことになる。どうなんだろう。

報告してみた

MozillaのBugzillaV8のIssueに、「JSCがなんかやってるらしいよ」と書いてみた。
まずMozillaの反応。

Interesting. I knew about the former, but the latter is much newer. In light of that, perhaps we should be changing, possibly...

the formerIE9のこと(バグで言及されてなかったのでついでにしてみた)。変えることになるかもしれないねえ…と。
続いてV8のほう。

Thanks for pointing this out! Currently, no stable version of any of the major browsers support the standard compliant behavior by default, which is why we are very hesitant to be the first to "fix" this and risk problems due to incompatibility with ES3. We will however be closely observing how this turns out once this change to JSC is shipped with the next version of Safari.

「どのメジャーブラウザの安定版もデフォルトでサポートしてないし」ってところにIE9モード…と思ったりもするけれど、でも「直す」というのにはやっぱり抵抗はありますよね。Safariがどうなるかを見て判断すると。

追記 (2012-08-15): Safari 6のリリースによりV8も追従

先月リリースされたSafari 6には変更が取り込まれ、0から始まっても8進数として扱われなくなった。

というわけでV8に報告してみた。そしたら4日後に反映された。このままいくとChrome 23から挙動が変わる模様。

追記 (2013-01-30): Firefox 21でSpiderMonkeyも追従

mozilla-centralにもチェックインされて、FirefoxもES5での仕様に準拠するようになった。

Firefox 21からこの挙動になりそう。

追記 (2013-08-16): OperaChromiumベースになったので追従→すべてのエンジンが対応

残ったOperaも、Chromiumベースになったことで、ES5の仕様に準拠するようになっている。

IE8がまだ結構多いだろうし、Androidブラウザもあるので、まだ安心して第二引数を省けるわけではなさそう。そこまでコードがごちゃごちゃするわけでもないので、基本はparseInt(str, 10) って書くのが、まだしばらくベストプラクティスのままなんだろう。