23

JavaScript で、8 進数の文字列が 10 進数としてキャストされるのはなぜですか? Number()orを使用して 16 進リテラル文字列をキャストできますが+、8 進数ではないのはなぜですか?

例えば:

1000 === +"1000" // -> true
0xFF === +"0xFF" // -> true
0100 === +"0100" // -> false - +"0100" gives 100, not 64

で解析できることはわかっparseInt("0100" [, 8])ていますが、16 進数と 10 進数のようにキャストが機能しない理由を知りたいです。

また、厳密モードで ECMAScript 5th Edition から 8 進リテラルが削除される理由を知っている人はいますか?

4

3 に答える 3

24

質問に少し遅れましたが、良い答えができると思います。

受け入れられた答えは、あなたが実際に知っていること以上のことを教えてくれず、質問自体に言及していNumber(value)ます。+valueparseInt(value)

重要なのは、型変換解析の間に意味的な違いがあることを知ることです。

8 進数の文字列が 10 進数としてキャストされるのはなぜですか?

Function ( ) および単項演算子( ) として呼び出されるNumber コンストラクターは、内部演算を使用するためです。これらの構造の目的は型変換です。Number(value)++valueToNumber

ToNumber文字列型に適用されると、 と呼ばれる特別な文法生成が使用されStringNumericLiteralます。

このプロダクションは、10 進リテラルと 16 進整数リテラルのみを保持できます。

...

StrNumericLiteral :::
   StrDecimalLiteral
   HexIntegerLiteral

...

この文法と「通常」の文法の間には意味上の違いもありますNumericLiterals

A StringNumericLiteral:

  • 空白および/または行末記号が前後にある場合があります。
  • つまり、10 進数には先頭の 0 桁がいくつあってもかまいません。八進数なし!
  • つまり、10 進数の前に + または - を付けて、その符号を示すことができます。
  • 空または空白のみを含むものは +0 に変換されます。

次にparseIntandparseFloat関数を使用します。

これらの関数の目的は明らかにparsingであり、これは型変換とは意味的に異なります。次に例を示します。

parseInt("20px");     // 20
parseInt("10100", 2); // 20
parseFloat("3.5GB");  // 3.5
// etc..

のアルゴリズムが ECMAScript 5th Edition 仕様で変更されたことに言及する価値がありますparseInt。数値の基数が先行ゼロを持つためだけに 8 進数として解釈されなくなりました。

parseInt("010"); // 10, ECMAScript 5 behavior
parseInt("010"); // 8,  ECMAScript 3 behavior

ご覧のとおり、これにより ES3 と ES5 の実装間の動作に互換性がなくなりました。問題を回避するために、いつものように基数引数を使用することをお勧めします。

2 番目の質問:

ECMAScript 5th Edition の厳密モードで 8 進リテラルが削除されるのはなぜですか?

実際、この 8 進リテラルを取り除く取り組みは 1999 年から行われています。8 進リテラル生成 (OctalIntegerLiteralおよび) は、 ECMAScript 3rd Edition 仕様以降、 sOctalEscapeSequenceの文法から削除されました。古いバージョンとの後方互換性( ES5 にも含まれています) のために含まれている可能性があります。標準の。NumericLiteral

実際、それらはすべての主要な実装に含まれていますが、技術的には、ES3 または ES5 に準拠した実装では、非規範的であると説明されているため、それらを含めないことを選択できます。

これが最初のステップでした。現在、ECMAScript 5 Strict Modeではそれらを完全に禁止しています。

しかし、なぜ?

それらはエラーが発生しやすい機能であると考えられていたため、実際、過去には、暗黙の8進数の同じ問題と同じように、意図しない、またはキャッチするのが難しいparseIntバグを引き起こしました。

現在、厳密モードでは、8 進リテラルはSyntaxError例外を引き起こします — 現在のところ、Firefox 4.0 Betas でのみ観測可能です —。

于 2010-10-04T07:16:28.237 に答える
4

あなたは実際に適切な意味でキャストを実行していないので(JSにはキャストがありません)-それは単に型ジャグリングです。

Javascriptにリテラルがあり、その上でメソッドを実行すると、オブジェクトがバックグラウンドで作成されます。

"foo".toUpperCase()たとえば、大まかに次のように見えるコードの評価に置き換えられますnew String( "foo" ).toUpperCase();

文字列は単項演算子で評価できないため+、JSは文字列を数値に変換します-そしてそれは使用しません-parseInt()またはparseFloat()内部的に-あなたが推測したように-それはを使用しますNumber()

したがって、表示される値は、Number()の戻り値から表示される値であり、8進数を想定していないように見えます。

于 2010-03-30T19:21:58.350 に答える
2

ES5 で 8 進数のサポートが削除された理由を詳しく説明すると、初心者やプログラマーではない人にとって、構文が予期しないものであることが主な理由です。一連の数値 (おそらく追加される) を垂直方向に整列することを想像してください。たとえば、先行ゼロを使用してそれらを整列させます。たとえば、数値が 8 または 9 を使用しない場合、それらは 8 進数として扱われることになります。明らかな理由もなく突然、コードが雑草の中に消えてしまいました! これが、8 進数のサポートが削除された理由です。そのような不幸を引き起こさないのであれば、別の 8 進構文がいつか追加されるかもしれません (私は0o7551 つのストローマン アイデアとして見たことを覚えていると思います) が、今のところ 8 進法はありません。

過去の回答で指摘された互換性のないparseInt変更について: この変更を行った実装はありません。ES5 はほとんど現実に基づいています。その新機能は通常、新機能を使用しようとしない既存のコードを壊しません (もちろん、新しいコードは、その使用の一部として既存のコードを壊さないように、新機能を使用する際に注意する必要があります)。その非互換性もほとんど無視できるか、実際の実装では互換性の理由から仕様を軽々しく無視しているため、無関係です。しかし、すべての非互換性が十分に根拠があるわけではありません。一部は、調和というよりは野心的なものです。への変更parseIntは、意欲的な変更の一例です。明示的な基数なしで、8 進数の構文を期待する既存のコードを壊します。

数日間、SpiderMonkey (Mozilla の JavaScript エンジン) はparseInt、厳密モードのコードから呼び出された場合は 8 進数を無視し、厳密モードのコードから呼び出されていない場合は 8 進数をサポートするように、途中で変更を実装しました。これは ES5 が望んでいるものに近いですが、厳密でないコードを厳密なモードに変換することの明らかな障害であり、ユーザーを混乱させる可能性が高く、おそらく実装者にとって最も興味深いことに、これは ES5 で実装できなかったことを意味しますparseInt。 JavaScript 自体 (関数の呼び出しの厳密さを調べる方法が仕様にないため) は、将来のある時点で (攻撃対象領域を減らし、実装を容易にするなどの目的で) 望ましいかもしれません。したがって、依存関係を元に戻しました。(私は作るためのパッチを書きましたparseInt発信者依存であり、元に戻すためにパッチを見直しました。これは、私の最初のパッチが到着した後のさらなる議論によって生み出されました。) parseIntは再び ES3 に準拠し、そのままの Web を考えると、ES5 のセマンティクスはおそらくそれと互換性がありません。私たちが変わることを疑います。したがって、他の人も変わるとは思えません。(また、ES5 が .NET で暗黙的な 8 進構文を禁止することを望んでいるという、Web の非互換性の程度に関する私たちの推定parseInt、そしておそらく私たちの他の理由にも同意してくれると確信しています。きっと彼らは従うだろうし、そうしない方が賢明だと私は思う.)

于 2010-10-04T08:10:21.317 に答える