229

好吧,所以我在搞乱 parseInt 以查看它如何处理尚未初始化的值,我偶然发现了这个 gem。以下情况适用于任何 24 或以上的基数。

parseInt(null, 24) === 23 // evaluates to true

我在 IE、Chrome 和 Firefox 中对其进行了测试,它们都非常正确,所以我认为它一定在规范中的某个地方。快速谷歌搜索没有给我任何结果,所以我在这里,希望有人能解释一下。

我记得听过 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


tl;博士

解释为什么parseInt(null, 24) === 23是一个真实的陈述。

4

6 回答 6

243

它正在转换null为字符串"null"并尝试转换它。对于从 0 到 23 的基数,它没有可以转换的数字,因此它返回NaN. 在 24处,"n"第 14 个字母被添加到数字系统中。在 31处,"u"添加第 21 个字母,并且可以解码整个字符串。在 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
于 2011-06-23T20:03:06.113 回答
119

Mozilla告诉我们

函数 parseInt将其第一个参数转换为字符串,对其进行解析,并返回一个整数或 NaN。如果不是 NaN,则返回值将是第一个参数的十进制整数表示形式,该参数采用指定基数(基数)中的数字。例如,基数 10 表示从十进制数、8 位八进制数、16 位十六进制数等转换。对于大于 10 的基数,字母表中的字母表示大于 9 的数字。例如,对于十六进制数(以 16 为基数),使用 A 到 F。

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

所以,让我们考虑一下parseInt("null", 24)

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

现在,在提取小数点 23 后解析停止,因为在 base-24 系统中找不到"u"

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

(这就是为什么parseInt(null, 23)(和更低的基数)给你NaN而不是 23:"n"不在 base-23 系统中。)

于 2011-06-23T20:03:01.620 回答
79

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

来自15.1.2.2 parseInt (string , radix)

当调用 parseInt 函数时,会执行以下步骤:

  • 让 inputString 为 ToString(string)。
  • 令 S 是 inputString 的一个新创建的子字符串,由不是 StrWhiteSpaceChar 的第一个字符和该字符之后的所有字符组成。(换句话说,删除前导空格。)
  • 设符号为 1。
  • 如果 S 不为空且 S 的第一个字符是减号 -,则令 sign 为 -1。
  • 如果 S 不为空并且 S 的第一个字符是加号 + 或减号 -,则从 S 中删除第一个字符。
  • 令 R = ToInt32(radix)。
  • 让 stripPrefix 为真。
  • 如果 R ≠ 0,则 a. 如果 R < 2 或 R > 36,则返回 NaN。湾。如果 R ≠ 16,则让 stripPrefix 为假。
  • 否则,R = 0 a。令 R = 10。
  • 如果 stripPrefix 为真,则 a. 如果 S 的长度至少为 2,并且 S 的前两个字符是“0x”或“0X”,则从 S 中删除前两个字符并令 R = 16。
  • 如果 S 包含任何不是 radix-R 数字的字符,则令 Z 是 S 的子串,由第一个此类字符之前的所有字符组成;否则,令 Z 为 S。
  • 如果 Z 为空,则返回 NaN。
  • 令 mathInt 为由 Z 以 radix-R 表示法表示的数学整数值,使用字母 AZ 和 az 表示值为 10 到 35 的数字。(但是,如果 R 为 10 且 Z 包含超过 20 个有效数字,则每个有效数字根据实现的选择,第 20 位之后的数字可以替换为 0 数字;如果 R 不是 2、4、8、10、16 或 32,则 mathInt 可能是数学整数的依赖于实现的近似由 Z 以 radix-R 表示法表示的值。)
  • 令 number 为 mathInt 的 Number 值。
  • 返回符号×数字。

注意 parseInt 只能将字符串的前导部分解释为整数值;它忽略任何不能被解释为整数表示法的一部分的字符,并且没有给出任何此类字符被忽略的指示。

这里有两个重要的部分。我把它们都加粗了。所以首先,我们要搞清楚它的toString表示null是什么。我们需要Table 13 — ToString Conversions在第 9.8.0 节中查看该信息:

在此处输入图像描述

太好了,所以现在我们知道在toString(null)内部执行会产生一个'null'字符串。很好,但是它究竟如何处理在提供的基数内无效的数字(字符)?

我们往上看,15.1.2.2我们看到以下评论:

如果 S 包含任何不是 radix-R 数字的字符,则令 Z 是 S 的子串,由第一个此类字符之前的所有字符组成;否则,令 Z 为 S。

这意味着我们处理指定基数之前的所有数字并忽略其他所有数字。

基本上,做parseInt(null, 23)parseInt('null', 23). u导致两个l's 被忽略(即使它们是基数 23 的一部分)。因此,我们只能解析n,使整个语句同义parseInt('n', 23)。:)

无论哪种方式,好问题!

于 2011-06-23T20:17:34.280 回答
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 包含任何不是 radix-R 数字的字符,则令 Z 是 S 的子串,由第一个此类字符之前的所有字符组成;否则,令 Z 为 S。

这是确保 C 风格的整数文字像15L正确解析的部分,所以上面相当于

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

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

量子点

于 2011-06-23T20:04:39.993 回答
16

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

于 2011-06-23T20:03:37.607 回答
7

parseInt 使用字母数字表示,那么在 base-24 中“n”是有效的,但“u”是无效字符,那么 parseInt 只解析值“n”....

parseInt("n",24) -> 23

例如,试试这个:

alert(parseInt("3x", 24))

结果将是“3”。

于 2011-06-23T20:06:45.813 回答