为什么 parseInt(8,3) 等于 NaN,并且 parseInt(16,3) 等于 1?

194

我正在阅读这篇文章,但我对带有基数参数的parseInt章节中所写的内容感到困惑。

parseInt(_, 3)结果表

为什么parseInt(8, 3)NaN,而parseInt(16, 3)1

据我所知,8和16不是基于3的数字,因此parseInt(16, 3)也应该返回NaN

前十个基于3的自然数


4
又一个问题本应通过静态类型(或者至少不将整数隐式转换为字符串)解决的:P。 - Navin
4
这与静态与动态类型无关(正如你自己所指出的)。 这里的问题是弱类型而不是强类型。 - Sven Marnach
12
当我看到这个问题的标题时,心想:“可能是因为loljavascript吧。”看到答案后,我认为我的直觉基本上是正确的。 - Ben Millwood
3个回答

377

即使人们知道这一点,他们仍然经常犯错。 :-) 你之所以看到这个问题,是因为parseInt("1abc")返回1的原因相同:parseInt在遇到第一个无效字符时停止,并返回此时的内容。如果没有有效字符可解析,则返回NaN

parseInt(8, 3)表示“在基数3中解析"8"”(请注意,它将数字8转换为字符串;规范中的详细信息)。但在基数3中,单个数字只有012。这就像要求它在八进制中解析"9"。由于没有任何有效字符,所以得到了NaN

parseInt(16, 3) 要求将 "16" 以基数3解析。它可以解析 1,因此解析了它,然后停止在 6 上,因为无法解析它。所以返回值为 1


由于这个问题受到了很多关注并且可能在搜索结果中排名较高,因此以下是JavaScript中将字符串转换为数字的选项概述,包括它们各自的特点和应用(摘自我在SO上的另一个答案):

  • parseInt(str[, radix]) - 把字符串开头尽可能多的部分转换成整数,忽略末尾的额外字符。所以parseInt("10x")10x被忽略了。支持可选的进制(数字基数)参数,所以parseInt("15", 16)21(16进制中的15)。如果没有进制,则假定为十进制,除非字符串以0x(或0X)开头,此时会跳过它们并假定为16进制。 (一些浏览器曾经将以0开头的字符串视为八进制;该行为从未被指定,并且在ES5规范中被明确禁止。)如果找不到可解析的数字,则返回NaN

  • parseFloat(str) - 与parseInt类似,但处理浮点数并仅支持十进制。同样,在字符串上的额外字符会被忽略,因此parseFloat("10.5x")10.5x被忽略)。由于只支持十进制,因此parseFloat("0x15")0(因为解析在x处结束)。如果找不到可解析的数字,则返回NaN

  • 一元+,例如+str - (例如,隐式转换)使用浮点数和JavaScript的标准数字表示法(仅包含数字和小数点=十进制;0x前缀=十六进制;0o前缀=八进制[ES2015+];某些实现将其扩展为将前导0视为八进制,但不在严格模式下)。+"10x"NaN,因为x忽略了。 +"10"10+"10.5"10.5+"0x15"21+"0o10"8 [ES2015+]。有一个陷阱:+""0,而不是像你可能期望的那样是NaN

  • Number(str) - 与隐式转换(例如,像上面的一元+)完全相同,但在某些实现上速度较慢。 (虽然这不太可能有影响。)


8
所以 parseInt 首先在第一个参数上使用 toString?那很有道理。 - evolutionxbox
16
是的,这是parseInt算法的第一步:http://www.ecma-international.org/ecma-262/7.0/index.html#sec-parseint-string-radix - T.J. Crowder
5
我猜123e-2会得到1,因为它首先变成1.23,然后解析停在小数点处。 - ilkkachu
6
「这是人们经常会被绊倒的事情,即使他们知道它也一样。」-> 我是唯一一个认为这应该是个 bug 的吗?举个例子,在 Java 中进行相同操作会每次都返回 NumberFormatException。 - Wim Deblauwe
4
“parseInt”中的“那个”部分(将第一个参数强制转换为字符串)很有道理。 “parseInt”的目的是将字符串解析为整数。因此,如果您提供的不是字符串,则先获取其字符串表示形式是有意义的。在此之后它所做的是完全不同的故事... - T.J. Crowder
显示剩余5条评论

54

出于同样的原因

>> parseInt('1foobar',3)
<- 1
文档中,parseInt接受一个字符串。如果字符串不是一个字符串,则它会被转换为字符串。因此,168'1foobar'首先被转换为字符串。然后,如果parseInt在指定基数中遇到一个非数字字符,它将忽略它和所有后续字符,这意味着它会尽可能地进行转换。其中的68foobar都被忽略,只有前面的部分被转换。如果没有任何内容,将返回NaN

0
/***** Radix 3: Allowed numbers are [0,1,2] ********/
parseInt(4, 3); // NaN - We can't represent 4 using radix 3 [allowed - 0,1,2]

parseInt(3, 3); // NaN - We can't represent 3 using radix 3 [allowed - 0,1,2]

parseInt(2, 3); // 2   - yes we can !

parseInt(8, 3); // NaN - We can't represent 8 using radix 3 [allowed - 0,1,2]

parseInt(16, 3); // 1  
//'16' => '1' (6 ignored because it not in [0,1,2])    

/***** Radix 16: Allowed numbers/characters are [0-9,A-F] *****/ 
parseInt('FOX9', 16); // 15  
//'FOX9' => 'F' => 15 (decimal value of 'F')
// all characters from 'O' to end will be ignored once it encounters the out of range'O'
// 'O' it is NOT in [0-9,A-F]

更多例子:

parseInt('45', 13); // 57
// both 4 and 5 are allowed in Radix is 13 [0-9,A-C]

parseInt('1011', 2); // 11 (decimal NOT binary)

parseInt(7,8); // 7
// '7' => 7 in radix 8 [0 - 7]

parseInt(786,8); // 7 
// '78' => '7' => 7 (8 & next any numbers are ignored bcos 8 is NOT in [0-7])

parseInt(76,8); // 62 
// Both 7 & 6 are allowed '76' base 8 decimal conversion is 62 base 10 

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