23

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

例如:

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

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

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

4

3 回答 3

24

我的问题有点晚了,但我想我可以给出一个很好的答案。

接受的答案并没有告诉你更多你实际知道的东西,并在问题本身中提到:Number(value)作为+value但不是作为parseInt(value).

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

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

因为在幕后调用为 Function ( Number(value)) 的 Number 构造函数和一元+运算符( ) 使用内部操作。这些构造的目的是类型转换+valueToNumber

ToNumber应用于字符串类型时,会使用一种特殊的语法产生式,称为StringNumericLiteral.

这个产生式只能包含十进制文字和十六进制整数文字:

...

StrNumericLiteral :::
   StrDecimalLiteral
   HexIntegerLiteral

...

这种语法与“正常”的语法之间也存在语义差异NumericLiterals

StringNumericLiteral

  • 可以在空格和/或行终止符之前和/或之后。
  • 也就是说,十进制可以有任意数量的前导 0 数字。没有八进制!
  • 即十进制可以在前面加上 + 或 - 来表示它的符号。
  • 即为空或仅包含空格将转换为 +0。

现在我将使用parseIntandparseFloat函数。

这些函数的目的显然是解析,这在语义上与类型转换不同,例如:

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 实现之间的行为不兼容,并且一如既往地建议使用 radix 参数,以避免任何可能的问题。

现在你的第二个问题:

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

实际上,这种摆脱八进制文字的努力始于 1999 年。自ECMAScript 第 3 版规范OctalIntegerLiteral以来,八进制文字产生式 (和OctalEscapeSequence) 已从NumericLiterals的语法中删除,它们可能被包括在内以与旧版本向后兼容也在 ES5 中)的标准。

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

那是第一步,现在 ECMAScript 5严格模式完全不允许它们。

但为什么?

因为它们被认为是一个容易出错的特性,事实上,在过去它们会导致无意难以捕获的错误——就像隐式八进制的相同问题一样parseInt

现在,在严格模式下,八进制文字将导致SyntaxError异常——目前只能在 Firefox 4.0 Beta 中观察到——。

于 2010-10-04T07:16:28.237 回答
4

因为您实际上并没有在正确的意义上执行强制转换(JS 没有强制转换) - 它只是类型杂耍。

当您在 Javascript 中有任何文字并对其执行方法时,会在幕后为您创建一个对象。

"foo".toUpperCase()例如,被大致如下所示的代码评估所取代new String( "foo" ).toUpperCase();

由于字符串不能用一元运算符求值+,JS 将你的字符串转换为一个数字——它不使用parseInt()parseFloat()在内部——你猜对了——它使用Number().

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

于 2010-03-30T19:21:58.350 回答
2

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

关于parseInt过去回复中提到的不兼容更改:没有实现进行此更改,我怀疑没有实现会进行此更改。ES5 主要基于现实。它的新功能通常不会破坏现有代码(除了新代码在使用新功能时当然必须注意不要破坏现有代码作为该使用的一部分),它不会尝试使用新功能。它的不兼容性也几乎可以忽略不计,或者它们无关紧要,因为现实世界的实现出于兼容性原因而轻率地忽略了规范。但并非所有的不兼容都是有充分根据的:有些比协调更令人向往。改变parseInt是一个理想的改变的例子。它破坏了需要八进制语法的现有代码,没有明确的基数,

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

于 2010-10-04T08:10:21.317 回答