今天我偶然发现了一个奇怪的(在我看来)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。任何人都可以向我解释为什么不呢?为什么有人希望这个函数表现得像这样(即使它不是传递的字符串的精确表示,也返回一个整数)?
答案 0 :(得分:43)
parseInt
读取输入,直到遇到无效字符,然后使用它在该无效字符之前读取的任何有效输入。考虑:
parseInt("17days", 10);
这将使用输入17
并忽略无效d
之后的所有内容。
如果[输入字符串] S包含任何不是基数-R数字的字符,那么让Z [字符串为整数]是S的子字符串,由第一个这样的字符之前的所有字符组成强>;否则,让Z为S.
在您的示例中,s
是16个字符的无效字符,因此parseInt
仅使用前导d
。
至于为什么包含了这种行为:没有办法确定,但这很可能是尝试从C中重现strtol
(字符串到长)的行为标准库。来自strtol(3)
man page:
...字符串以明显的方式转换为long int值,停在第一个字符,该字符不是给定基数中的有效数字。
由于parseInt
和strtol
都被指定为忽略前导空格,并且它们都可以接受十六进制的前导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)
的值不同。
虽然字符串是相同的字符串,但不同的基数将返回不同的数字。
查看实际操作
"A"
&#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
。之后它会停止解析,因为下一个字符不是十六进制因此结果。