为什么parseInt(' dsff66',16)返回13?

时间:2014-09-24 12:50:06

标签: javascript parseint

今天我偶然发现了一个奇怪的(在我看来)JavaScript的情况。我将非十六进制字符串传递给parseInt函数,其基数为16,并且...得到了结果。 我希望函数抛出某种异常或至少返回NaN,但它成功解析它并返回一个int。

我的电话是:

var parsed = parseInt('dsff66', 16); // note the 's' in the first argument
document.write(parsed);

,结果是:13

我注意到它“停止”解析第一个不属于第二个参数中指定的数字系统的字符,所以调用parseInt('fg',16)我会得到15。< / p>

在我看来,它应该归还NaN。任何人都可以向我解释为什么不呢?为什么有人希望这个函数表现得像这样(即使它不是传递的字符串的精确表示,也返回一个整数)?

4 个答案:

答案 0 :(得分:43)

parseInt读取输入,直到遇到无效字符,然后使用它在该无效字符之前读取的任何有效输入。考虑:

parseInt("17days", 10);

这将使用输入17并忽略无效d之后的所有内容。

来自ECMAScript specification

  

如果[输入字符串] S包含任何不是基数-R数字的字符,那么让Z [字符串为整数]是S的子字符串,由第一个这样的字符之前的所有字符组成;否则,让Z为S.

在您的示例中,s是16个字符的无效字符,因此parseInt仅使用前导d

至于为什么包含了这种行为:没有办法确定,但这很可能是尝试从C中重现strtol(字符串到长)的行为标准库。来自strtol(3) man page

  

...字符串以明显的方式转换为long int值,停在第一个字符,该字符不是给定基数中的有效数字

由于parseIntstrtol都被指定为忽略前导空格,并且它们都可以接受十六进制的前导0x这一事实进一步支持此连接(在某种程度上)值。

答案 1 :(得分:22)

  

为什么有人希望这个函数表现得像这样(即使它不是传递的字符串的精确表示,也返回一个整数)?

因为大多数时候(到目前为止)你正在处理基数为10的数字,在这种情况下,JS只能强制转换 - 而不是解析 - 将字符串转换为数字。 (编辑:显然不仅仅是基础10;请参阅下面的更新。)

由于JS是动态类型的,因此一些字符串可以正常工作,而不需要您做任何工作。例如:

 "21" / 3;   // => 7
 "12.4" / 4; // => 3.1

不需要parseInt,因为"21""12.4"已经是数字。但是,如果字符串是"12.4xyz",那么在分割时你确实会得到NaN,因为这绝对不是一个数字,不能隐式地强制转换或强制转换为一个数字。

您还可以使用Number(someString)将字符串显式“强制转换”为数字。 虽然它也只支持基数10,但它确实会为无效字符串返回NaN

因为JS已经有隐式和显式类型转换/转换/强制,parseInt的角色不是另一种类型转换函数。

parseInt的角色是,解析函数。一种尽力理解其输入并返回其能力的函数。这是因为当你有一个你无法刚出演的字符串时,因为它不是很完美的数字。 (而且,就像JS的基本语法一样,它让人想起C,因为apsillers的回答很好地解释了。)

由于它是一个解析器,而不是一个转换函数,因此它具有能够处理除10之外的其他基础的附加功能。

现在,您可能会问为什么没有严格的投射函数来处理非基数为10的数字,并会抱怨您想要的,但是......嘿,那里只是不是。 JS的设计者刚刚决定parseInt就足够了,因为,再次,0x63%的时间,你正在处理基数10.

最接近你可以进行“施放”可能是非常黑客的事情:

var hexString = "dsff66";
var number = eval("0x" + hexString); // attempt to interpret as a hexadecimal literal

会抛出SyntaxError,因为0xdsff66不是有效的十六进制文字。

更新:正如Lekensteyn在评论中指出的那样,JS似乎也正确地转换了0x - 前缀十六进制字符串。我不知道这一点,但事实上这似乎有效:

1 * "0xd0ff66"; // => 13696870
1 * "0xdsff66"; // => NaN

这使得它成为将十六进制字符串转换为数字的最简单方法 - 如果无法正确表示,则获取NaN

同样的行为适用于Number(),例如Number("0xd0ff66")返回一个整数,Number("0xdsff66")返回NaN

(的 /更新

或者,您可以事先检查字符串,并在需要时返回NaN

function hexToNumber(string) {
  if( !/^(0x)?[0-9a-f]+$/i.test(string) ) return Number.NaN;
  return parseInt(string, 16);
}

答案 2 :(得分:12)

在此特定情况下parseInt()"A""F"的字母解释为hexadecimal并将其解析为十进制数字。这意味着d将返回13

parseInt()做什么

  • parseInt("string", radix)将字符串中的数字和字母解释为十六进制(取决于基数)到数字。

  • parseInt()仅从字符串的开头解析数字或字母为十六进制,直到无效字符为十六进制。

  • 如果parseInt()无法在字符串parseInt()的开头找到任何数字或字母为十六进制,则会返回 NaN

  • 如果未定义基数,则基数为10

  • 如果字符串以"0x"开头,则基数为16

  • 如果基数定义为0,则基数为10

  • 如果基数为1,则parseInt()会返回NaN。

  • 如果基数为2,则parseInt()仅解析"0""1"

  • 如果基数为3,则parseInt()仅解析"0""1""2"。等等。

  • parseInt()"0"解析为0,如果结果后面没有数字,则删除0如果后面有数字。例如&#34; 0&#34;返回0和&#34; 01&#34;返回1.

  • 如果基数为11,则parseInt()仅解析以"0""9"和/或字母"A"之间的数字开头的字符串。

  • 如果基数为12,则parseInt仅解析以"0""9"和/或字母"A"和{{1}的数字开头的字符串等等。

  • 最大基数为"B",它将解析以36"0"的数字开头的字符串和/或"9"到{{1}的字母}}

  • 如果字符被解释为十六进制多于一个,则每个字符将具有不同的值,尽管这些字符是相同的字符。例如"A"第一个"Z"与第二个parseInt("AA", 11)的值不同。

  • 虽然字符串是相同的字符串,但不同的基数将返回不同的数字。

查看实际操作

&#13;
&#13;
"A"
&#13;
&#13;
&#13;

答案 3 :(得分:6)

For radices above 10, the letters of the alphabet indicate numerals greater than 9. For example, for hexadecimal numbers (base 16), A through F are used.

在字符串dsff66中,d是一个十六进制字符(即使字符串是非十六进制),它符合基数类型,相当于数字13。之后它会停止解析,因为下一个字符不是十六进制因此结果。