parseInt(null, 24) === 23... 等等,这是什么意思?

230

好的,我在玩parseInt时想看看它如何处理尚未初始化的值,然后我偶然发现了这个宝石。以下情况适用于任何24或更高进制。

parseInt(null, 24) === 23 // evaluates to true
我在IE、Chrome和Firefox中进行了测试,它们都会弹出true,因此我认为这可能是规范中的一部分。快速谷歌搜索没有给我任何结果,所以我在这里希望有人能解释一下。
我记得听过Crockford的一篇演讲,他说typeof null === "object",因为一个疏忽导致Object和Null在内存中具有相似的类型标识符或类似的东西,但我现在找不到那个视频了。
试试这个:http://jsfiddle.net/robert/txjwP/ 编辑更正:更高的基数返回不同的结果,32返回785077 编辑2来自zzzzBov:[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745 简而言之,请解释为什么parseInt(null, 24) === 23是一个真实的陈述。

51
有意思,JavaScript 总是让你保持警觉。 - FishBasketGordo
2
数据点:alert(parseInt(null, 34) === 23)产生了false - Stephen P
2
警告(parseInt(null,26)===23);也会产生真的?!? - Petar Ivanov
7
很抱歉,您提供的翻译内容并不完整或明确。请提供更具体和清晰的信息以便我能够更好地帮助您进行翻译。 - zzzzBov
1
作为额外的说明,将undefined作为第一个参数返回30年代的奇怪结果。 - zzzzBov
显示剩余6条评论
6个回答

244
它将 null 转换为字符串 "null" 并尝试转换它。 对于基数从0到23,它无法转换任何数字,因此返回 NaN。 在24时,第14个字母"n" 被添加到数字系统中。在31时,第21个字母"u" 被添加,并且整个字符串可以被解码。从37开始,再也无法生成任何有效的数字集,并返回NaN。
js> parseInt(null, 36)
1112745

>>> reduce(lambda x, y: x * 36 + y, [(string.digits + string.lowercase).index(x) for x in 'null'])
1112745

3
谁说它正在使用 toString() - Ignacio Vazquez-Abrams
3
@Ignacio,实际上,我错了。我没有意识到37是指基数。对此感到抱歉。 - Mike Samuel
3
@Robert,不是的,我当时有些困惑,以为他宣称了其他的事情。这个答案是正确的。对于我的错误,向大家道歉。 - Mike Samuel
19
我认为这个回答还需要一些参考文献支持。虽然它完全正确,但它只是一个大的断言... - Lightness Races in Orbit
4
@Tomalak - 请检查我的回答中所有的参考文献。这个回答是正确的(也是第一个),所以我认为它应该保持为被接受的答案。虽然解释发生了什么并不会有任何损失 ;) - David Titarenco
显示剩余13条评论

121

Mozilla 告诉我们:

函数parseInt 将其第一个参数转换为字符串,对其进行解析,然后返回一个整数或NaN。如果不是NaN,则返回值将是第一个参数在指定基数(进制)中作为数字的十进制整数表示。例如,基数10表示要从十进制数转换,8表示八进制,16表示十六进制,以此类推。对于大于10的进制数,字母表中的字母表示大于9的数字。例如,对于十六进制数字(基数16),使用A到F。

规范中,15.1.2.2/1告诉我们,使用内置的ToString进行字符串转换,这会产生"null"(不要与toString混淆,它会产生"[object Window]"!)。

因此,让我们考虑parseInt("null", 24)

当然,这不是完全的24进制数字字符串,但是"n"是:它是十进制23

现在,在提取出十进制23之后,解析将停止,因为在基数为24的系统中找不到"u"

如果S包含任何不是基数R数字的字符,则令Z为S中第一个这样的字符之前的所有字符组成的子字符串;否则,令Z为S。 [15.1.2.2/11]

这就是为什么parseInt(null, 23)(以及低进制)会返回NaN,而不是23的原因:"n" 不在基数为23的系统中。


2
这是parseInt非常悲惨的行为(我在想为什么它没有被设计成在这些情况下抛出异常)。当我可以时,我更喜欢使用NUMBER()。 - Grijesh Chauhan

81

Ignacio Vazquez-Abrams是正确的,但是让我们看看它是如何工作的...

来自15.1.2.2 parseInt (string , radix)

当调用parseInt函数时,将执行以下步骤:
- 让inputString成为ToString(string)。 - 让S成为一个新创建的子字符串,它由不是StrWhiteSpaceChar的第一个字符和该字符后面的所有字符组成。(换句话说,删除前导空格。) - 让sign等于1。 - 如果S不为空且S的第一个字符是减号-,则让sign等于-1。 - 如果S不为空且S的第一个字符是加号+或减号-,则从S中删除第一个字符。 - 让R等于ToInt32(radix)。 - 让stripPrefix为true。 - 如果R ≠ 0,则a. 如果R < 2或R > 36,则返回NaN。b. 如果R ≠ 16,则让stripPrefix为false。 - 否则,R = 0 a. 让R = 10。 - 如果stripPrefix为true,则a. 如果S的长度至少为2且S的前两个字符是“0x”或“0X”,则从S中删除前两个字符并让R = 16。 - 如果S包含任何不是基数为R的数字字符,则让Z成为S中所有这些字符之前的子字符串;否则,让Z成为S。 - 如果Z为空,则返回NaN。 - 让mathInt成为用字母A-Z和a-z表示值为10到35的数字的基数为R的数学整数值,它由Z在基数为R的符号表示法中表示。(但是,如果R为10且Z包含超过20个有效数字,则实现可以选择将第20个之后的每个有效数字替换为0数字;如果R不是2、4、8、10、16或32,则mathInt可能是对在基数为R的符号表示法中表示的数学整数值的实现依赖近似。) - 让number成为mathInt的Number值。 - 返回sign × number。
注意:parseInt只能解释字符串的前导部分作为整数值;它会忽略任何不能解释为整数符号的字符,并且不会指示任何这样的字符被忽略。

这里有两个重要的部分。我将它们都加粗了。首先,我们需要找出nulltoString表示形式是什么。我们需要查看第9.8.0节中的Table 13 — ToString Conversions以获取该信息:

enter image description here

很好,现在我们知道执行toString(null)内部会产生一个'null'字符串。那么,它如何处理不在提供进制中的数字(字符)呢?
我们可以看到上面的15.1.2.2中有以下说明:
如果S包含任何不是基数R数字的字符,则让Z成为S中第一个这样的字符之前的所有字符组成的子串;否则,让Z成为S。
这意味着我们处理指定进制之前的所有数字,并忽略其他所有内容。
基本上,执行parseInt(null, 23)与执行parseInt('null', 23)是一样的。 u导致两个l被忽略(即使它们是23进制的一部分)。 因此,我们只能解析n,使整个语句等同于parseInt('n', 23)。 :)
无论哪种方式,都是个好问题!

34
parseInt( null, 24 ) === 23

等同于

parseInt( String(null), 24 ) === 23

等同于

parseInt( "null", 24 ) === 23

24进制的数字包括0、1、2、3、4、5、6、7、8、9、a、b、c、d、e、f,...、n。

语言规范说明:

  1. 如果字符串S中包含任何一个非基数为R的数字,则令Z为在第一个这样的字符之前的所有字符组成的子串;否则令Z为S本身。

这个规则保证了C语言风格的整数字面量(例如15L)能够正确解析,因此上述内容等价于:

parseInt( "n", 24 ) === 23

"n" 是上述数字列表中的第23个字母。

证毕。


16

我猜测null会被转换成字符串"null"。所以在'base24'(同样适用于'base25'+)中,n实际上是23,而u在'base24'中是无效的,因此字符串的其余部分null将被忽略。这就是为什么输出是23,直到u在'base31'中变得有效。


7

parseInt使用字母数字表示,然后在24进制中,“n”是有效的,但“u”是无效字符,因此parseInt仅解析值“n”....

parseInt("n",24) -> 23

作为一个示例,尝试使用这个:
alert(parseInt("3x", 24))

结果将会是“3”。

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