为什么八进制文字不能作为字符串转换为数字?

时间:2010-03-30 18:54:53

标签: javascript ecmascript-5

在JavaScript中,为什么将八进制数字串转换为十进制数?我可以使用Number()+转换十六进制文字字符串,为什么不使用八进制?

例如:

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

我知道我可以使用parseInt("0100" [, 8])解析,但我想知道为什么转换不像hex和dec数字那样工作。

另外,有没有人知道为什么在严格模式下从ECMAScript第5版中删除了八进制文字?

3 个答案:

答案 0 :(得分:24)

我对这个问题有点迟,但我想我能给出一个好的答案。

接受的答案并没有告诉你更多你真正了解的内容,并在问题中提及:Number(value)作为+value但不作为parseInt(value)

关键是要知道类型转换解析之间存在语义差异

  

为什么将八进制数字串转换为十进制数?

因为幕后的Number constructor called as a FunctionNumber(value))和Unary + Operator+value)使用ToNumber内部操作。这些构造的目的是类型转换

ToNumber is applied to the String Type使用特殊的语法产生时,称为StringNumericLiteral

此制作只能包含十进制文字和十六进制整数文字:

...

StrNumericLiteral :::
   StrDecimalLiteral
   HexIntegerLiteral

...

这种语法与“普通”NumericLiterals的语法之间也存在语义差异。

A StringNumericLiteral

  • 可以在空格和/或行终止符之前和/或之后。
  • 即十进制可以包含任意数量的前导0位数。 no octals!
  • 即十进制可以在前面加+或 - 表示其符号。
  • 那是空的或只包含空格会转换为+0。

现在我将使用parseIntparseFloat函数。

这些功能的目的显然是解析语义类型转换不同,例如:

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

值得一提的是,parseInt的算法在ECMAScript第5版规范中发生了变化,它不再将数字的基数解释为八进制只是因为有一个前导零:

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

如您所见,这在ES3和ES5实现之间的行为中引入了incompatibility,并且建议始终使用radix参数,以避免任何可能的问题。

现在你的第二个问题:

  

为什么在严格模式下从ECMAScript第5版中删除了八进制文字?

实际上,自1999年以来,这种摆脱八进制文字的努力就出现了。自{OctalIntegerLiteral以来,OctalEscapeSequence的语法中删除了八进制文字作品(NumericLiteralparseInt) 3}},他们可能会包含ECMAScript 3rd Edition specificationbackwards compatibility)旧版本的标准。

事实上,它们包含在所有主要实现中,但从技术上讲,符合ES3或ES5的实现可以选择不包含它们,因为它们被描述为非规范

这是第一步,现在ECMAScript 5 also in ES5完全不允许它们。

但为什么?

因为它们被认为是易出错的功能,事实上,在过去它们导致无意难以捕获错误 - 就像SyntaxError - 。

的隐式八进制问题一样

现在,在严格模式下,八进制文字将导致{{1}}异常 - 目前只能在Firefox 4.0 Betas中观察到。

答案 1 :(得分:4)

因为你实际上没有按照正确的意义执行施法(JS没有施法) - 它只是类型杂耍。

当你在Javascript中有任何文字并在其上制定方法时,会在幕后为你创建一个对象。

例如,

"foo".toUpperCase()被代码的评估所取代,大致看起来像new String( "foo" ).toUpperCase();

由于无法使用一元+运算符来评估字符串,JS会将您的字符串转换为数字 - 而且内部不使用parseInt()parseFloat() - 您猜对了 - 它使用Number()

因此,您看到的值是您从Number()的返回中看到的值,它似乎不会假设为八进制。

答案 2 :(得分:2)

详细说明为什么在ES5中删除了八进制支持,这主要是因为对于新手或非程序员来说,语法是意外的。想象一下,垂直对齐一系列数字(可能是被添加的),使用前导零来对齐它们,例如 - 如果你的数字不使用8或9,它们最终将被视为八进制。突然间你的代码在杂草中消失了,没有明显的原因!这就是为什么八角形支撑被移除的原因。如果它不会造成这样的不幸,我可能有一天会添加一个不同的八进制语法(我想我记得看到0o755作为一个稻草人的想法),但现在八进制已经出局了。

关于过去回复中提到的不兼容的parseInt更改:没有实施已进行此更改,我怀疑没有实现可以实现。 ES5主要基于现实。它的新功能通常不会破坏现有代码(当然新代码必须注意使用新功能而不是破坏现有代码作为使用的一部分),而不是尝试使用新功能。它的不兼容性几乎可以忽略不计,或者它们无关紧要,因为出于兼容性原因,现实世界的实现忽略了规范。但并非所有的不兼容性都是有根据的:有些人更有抱负而不是协调。对parseInt的更改是一个有抱负的变化的例子。它打破了现有的代码,这些代码需要八进制语法,而没有显式的基数,才能解析为八进制。

几天的时间SpiderMonkey(Mozilla的JavaScript引擎)实现了一个中途变化来制作parseInt,当从严格模式代码调用时,忽略八进制,并在未从严格模式代码调用时支持八进制。这更接近ES5的要求,但是将非严格代码转换为严格模式显然存在障碍,这可能会让用户感到困惑,而且 - 对于实施者来说可能最有趣 - 这意味着你无法实现{ JavaScript本身中的{1}}(因为在规范中没有办法检查一个调用函数的严格性),因为在未来的某个时间可能需要(减少攻击面,简化实现等)。所以我们解除了依赖。 (我编写了补丁以使parseInt依赖于呼叫者,我查看了补丁以撤消它,在我的初始补丁登陆后通过进一步的讨论产生。)parseInt现在再次符合ES3,并给出了网络原样,并且ES5的语义可能与它不兼容,我怀疑我们会改变。因此,我怀疑其他人也会改变。 (我也非常确定他们同意我们对网络不兼容程度的估计与ES5在parseInt中对隐式八进制语法的理想禁止,以及可能与我们的其他原因有关。即使我们是改变我不确定他们会跟随,我怀疑他们不聪明。)