我该如何解决JavaScript的parseInt八进制行为?

285

尝试在JavaScript中执行以下代码:

parseInt('01'); //equals 1
parseInt('02'); //equals 2
parseInt('03'); //equals 3
parseInt('04'); //equals 4
parseInt('05'); //equals 5
parseInt('06'); //equals 6
parseInt('07'); //equals 7
parseInt('08'); //equals 0 !!
parseInt('09'); //equals 0 !!

我刚刚吃了亏,原来JavaScript认为前导零表示一个八进制整数,而在八进制中没有"8""9",所以函数返回零。不管你喜欢还是不喜欢,这是有意设计的

有什么解决方法?

注意:为了完整起见,我将要发布一种解决方案,但它是我讨厌的解决方案,请发布其他/更好的答案。


更新:

JavaScript标准的第五版(ECMA-262)引入了一项破坏性变化,消除了这种行为。Mozilla有一个很好的说明


22
步骤1)为自己着想,始终包括前面答案中提到的基数以及Doug的书中提到的基数。步骤2)如果您真的想学习JavaScript,请获取Doug的书的副本。它是无价的。到目前为止,这是我最喜欢的书。以下是一篇评论:http://realtech.burningbird.net/learning-javascript/basics/javascript-the-good-parts - fuentesjr
9
在支持ECMAScript第5版标准的浏览器中(如Internet Explorer 9),除非要解析的数字已经以“0x”开头(例如“0xFF”),否则基数默认为10进制,即默认为“10”。希望有一天这个问题会成为遥远的记忆。 - Andy E
2
怎么样只用 +'08' === 8?没错!也许你在实际代码中确实需要 parseInt,但对于上述情况不需要。 - kojiro
1
a) 这不是一个错误,请修正标题 b) Number('08') - OnTheFly
4
可能值得指出的是,在第 5 版中,引入了一项破坏性更改,消除了这种行为。即使在第三版(13 年前),也“鼓励”实现不要这样做:“当基数为 0undefined 且字符串的数字以 0 数字开头但后面没有跟随 xX 时,实现可以自行决定将数字解释为八进制还是十进制。在这种情况下,鼓励实现将数字解释为十进制。”(我强调了)。 - T.J. Crowder
显示剩余4条评论
10个回答

333

这是一个常见的Javascript陷阱,解决方法很简单:

只需指定基数或“radix”,就像这样:(参考链接)

parseInt('08',10); // 8

您还可以使用Number

Number('08'); // 8

10
数字08需要加上引号。另外请注意,使用Number('08.123')将会输出8.123。如果你真的想要一个整数,请不要使用Number(或对输入进行模式匹配以确保只有整数)。 - Jason S
3
Number(08)在Firefox和IE中都会返回8。 - Paolo Bergantino
3
不属于ECMAscript标准。我正在JSDB上进行测试,它使用Spidermonkey 1.7(Firefox JS引擎),并且报错“08不是合法的ECMA-262八进制常量”。 - Jason S
5
仍需将“08”用引号括起来。这是因为“08”不符合ECMA-262标准,不能保证在没有警告、错误或特定指定行为的情况下成功运行。 - Jason S
老问题,但请注意Number与parseInt不同:https://dev59.com/Qm855IYBdhLWcg3w5IrZ#4090577 - Gary S. Weaver

44

如果您知道您的值将在有符号 32 位整数范围内,那么~~x在所有情况下都会得到正确的结果。

~~"08" === 8
~~"foobar" === 0
~~(1.99) === 1
~~(-1.99)  === -1

如果你查询二进制非运算符(~),规范要求对参数进行"ToInt32"转换,它将显式地将其转换为Int32,并强制将NaN值转换为零。

是的,这很不正规,但非常方便...


3
为什么需要两个操作,一个按位或就足够了吗? - Oleg V. Volkov
@Oleg,为什么只需要一个就足够了? - Sebastian
@Grodriguez,当|0中的0是一个字符串时呢? - Oleg V. Volkov
这个问题的核心是关于除了parseInt之外将字符串转换为整数的替代方法。因此,总是要考虑这一点。 - Grodriguez

24

根据parseInt文档,使用可选的进制参数来指定十进制数:

parseInt('08', 10); //equals 8
parseInt('09', 10); //equals 9

对我来说,这似乎是迂腐的、令人困惑的和冗长的(实际上,在每个parseInt中加一个额外的参数?),因此我希望有更好的方法。


7
如果您不喜欢冗长的语句,那么可以自己编写一个函数,调用内置函数并填入您始终保持不变的参数。 - Jason S
3
当然,因为在StackOverflow上每个人都会谴责你写parseIntB10函数。针对这个目的编写自己的包装函数是一个糟糕的想法。 - Stefan Kendall
2
@iftrue:我认为你没有理解我的观点。我个人不介意在任何地方都使用parseInt(someString,10)来确保我强制使用基数10。OP似乎不喜欢这种方法,所以我建议了一种替代方法,虽然我个人不会使用它,但也许它能满足他的需求。(这显然是JQuery背后的思想:通过添加额外的复杂性使其更方便。我不使用JQuery,但许多人发现它很有用。) - Jason S
6
在函数式表达式中方便使用parseInt非常重要,比如在["7","4","09","5"].map(parseInt);中使用。map方法将数组元素的索引作为第二个参数传递,如果不进行包装,则该参数会被parseInt解释为进制数。 - Plynx

12
function parseDecimal(s) { return parseInt(s, 10); }

编辑:如果你不喜欢每次调用parseInt()时都添加",10",那么自己编写函数来实现你真正想要的功能只是一种选择。它的缺点是这个非标准的函数对于你经常使用它的人来说更加方便,但或许会让其他人感到困惑。


10

指定基础数:

var number = parseInt(s, 10);

哇,你们好快。我甚至已经将我的答案复制到剪贴板上了。你们是直接插入互联网的吗,就像尼奥一样? - Portman
1
为什么它不默认为十进制呢? - Tom Gullen
2
可能是因为它的主要目的不是作为一个字符串转数字的函数,而是从一个基数为b的数字字符串中读取一个数字的函数。 - wnrph
3
默认使用十进制,但前导零是表示八进制的普遍方式。 - Nathan

7

如果没有第二个参数,用一个假定十进制的版本替换parseInt,这样做是不是很淘气?(注意-未经测试)

parseIntImpl = parseInt
parseInt = function(str, base){return parseIntImpl(str, base ? base : 10)}

2
是的,那样做是不好的——它会破坏依赖于标准行为的其他代码。 - jes5199
4
没错,但这种情况并不多见。而且这也不是标准行为——八进制支持是可选的。 - Andrew Duffy
3
你还要放弃十六进制支持,这不是可选的,对吗?这应该永远成立:parseInt("0xFFFFFF") === 16777215,但是由于你调皮的黑客技巧,它不再起作用了,变成了 parseInt("0xFFFFFF") === 0 - Timo
2
@jes5199 但是,随后的ECMAScript 5决定打破了依赖该行为的其他代码。 (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt#Octal_interpretations_with_no_radix) - Piyin

7
您也可以使用一元运算符(+)而不是使用parseFloat或parseInt。
+"01"
// => 1

+"02"
// => 2

+"03"
// => 3

+"04"
// => 4

+"05"
// => 5

+"06"
// => 6

+"07"
// => 7

+"08"
// => 8

+"09"
// => 9

并且为了保险起见

+"09.09"
// => 9.09

MDN 链接

一元正号运算符在其操作数之前,并返回它的操作数,但尝试将其转换为数字(如果它还不是数字)。虽然一元否定(-)也能够将非数字转换为数字,但一元正号是最快和首选的将某物转换为数字的方法,因为它不对数字执行任何其他操作。


5
这个十进制数怎么样?
('09'-0) === 9  // true

('009'-0) === 9 // true

4

如果您已经使用parseInt进行了大量编码,而且不想在每个数字后面添加",10",那么您可以重写函数以使基数10成为默认值:

window._oldParseInt = window.parseInt;
window.parseInt = function(str, rad) {
    if (! rad) {
        return _oldParseInt(str, 10);
    }
    return _oldParseInt(str, rad);
};

这可能会使后面的读者感到困惑,因此创建一个parseInt10()函数可能更加自说明。个人而言,我更喜欢使用简单的函数,而不是一直添加",10"——这只会增加出错的机会。


4
这个问题在最新的Chrome和Firefox(2019年)中都无法复制。
测试:

console.log(parseInt('08'));


1
是的,看起来终于有人决定八进制的东西带来的问题比解决的问题还要多。虽然我很讨厌成为那 0.01% 的人之一,他们实际上使用/依赖于任何具有前导 0 的内容将被解析为八进制的功能。 :) - aroth
1
这将会给一些人带来非常严重的错误! - aidan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接