parseInt (null, 24) === 23 ... подожди, что? - PullRequest
224 голосов
/ 23 июня 2011

Хорошо, поэтому я возился с parseInt, чтобы посмотреть, как он обрабатывает значения, которые еще не инициализированы, и наткнулся на этот драгоценный камень.Приведенное ниже относится к любому основанию 24 или выше.

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

Я проверил его в IE, Chrome и Firefox, и все они предупреждают об истинности, поэтому я думаю, что это должно быть где-то в спецификации.Быстрый поиск в Google не дал мне никаких результатов, поэтому я надеюсь, что кто-то может объяснить.

Я помню, как слушал речь Крокфорда, где он говорил 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 является верным утверждением.

Ответы [ 6 ]

235 голосов
/ 24 июня 2011

Преобразует 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
118 голосов
/ 24 июня 2011

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, поскольку "u" не найдено в системе base-24:

Если S содержит любой символ, который не цифра радикс-R, тогда пусть Z будет подстрока S, состоящая из всех персонажи перед первым таким персонаж; в противном случае пусть Z будет S. [15.1.2.2/11]

(И именно поэтому parseInt(null, 23) (и более низкие радиусы) дает вам NaN, а не 23: "n" не входит в систему base-23.)

79 голосов
/ 24 июня 2011

Игнасио Васкес-Абрамс верен, но давайте точно увидим , как это работает ...

С 15.1.2.2 parseInt (string , radix):

Когда вызывается функция parseInt, предпринимаются следующие шаги:

  • Пусть inputString будет ToString (строка).
  • Пусть S будет вновь созданной подстрокой inputString, состоящей из первого персонаж, который не является StrWhiteSpaceChar и все символы после этого персонажа. (В других слова, уберите начальные пробелы.)
  • Пусть знак будет 1.
  • Если S не пусто и первый символ S - знак минус -, пусть знак будет -1.
  • Если S не пусто, а первый символ S является знаком плюс + или знак минус -, затем уберите первый персонаж из S.
  • Пусть R = ToInt32 (основание).
  • Пусть stripPrefix будет истинным.
  • Если R ≠ 0, то а. Если R <2 или R> 36, то вернуть NaN. б. Если R ≠ 16, пусть stripPrefix быть ложным.
  • Иначе, R = 0 а. Пусть R = 10.
  • Если stripPrefix имеет значение true, тогда a. Если длина S не менее 2 и первые два символа S либо «0x» или «0X», затем удалите первый два символа из S и пусть R = 16.
  • Если S содержит любой символ, который не является цифрой радикс-R, то пусть Z будет подстрока S, состоящая из всех персонажи перед первым таким персонаж; в противном случае пусть Z будет S.
  • Если Z пусто, вернуть NaN.
  • Пусть mathInt будет математическим целочисленным значением, которое представлено Z в обозначениях radix-R, используя буквы A-Z и a-z для цифр со значениями 10 до 35. (Однако, если R равен 10 и Z содержит более 20 значимых цифры, каждая значащая цифра после 20-й может быть заменен 0 цифрой, по выбору реализации; и если R не равен 2, 4, 8, 10, 16 или 32, то математика может быть зависящее от реализации приближение до математического целочисленного значения, которое представлен Z в radix-R нотации.)
  • Пусть number будет числовым значением для mathInt.
  • Обратный знак × номер.

ПРИМЕЧАНИЕ parseInt может интерпретировать только ведущая часть строки как целочисленное значение; он игнорирует любой символы, которые нельзя интерпретировать как часть обозначения целого числа, и никаких указаний на то, что такие символы были проигнорированы.

Здесь есть две важные части. Я смел их обоих. Итак, прежде всего, мы должны выяснить, что такое toString представление null. Нам нужно взглянуть на Table 13 — ToString Conversions в разделе 9.8.0 для получения этой информации:

enter image description here

Отлично, теперь мы знаем, что выполнение toString(null) внутренне дает строку 'null'. Отлично, но как именно он обрабатывает цифры (символы), которые недопустимы в пределах предоставленного radix?

Мы смотрим выше на 15.1.2.2 и видим следующее замечание:

Если S содержит любой символ, который не цифра радикс-R, тогда пусть Z будет подстрока S, состоящая из всех персонажи перед первым таким персонаж; в противном случае пусть Z будет S.

Это означает, что мы обрабатываем все цифры PRIOR до указанного радиуса и игнорируем все остальное.

По сути, parseInt(null, 23) - это то же самое, что и parseInt('null', 23). u вызывает игнорирование двух l (даже если они являются частью радиуса 23). Следовательно, мы можем анализировать только n, делая весь оператор синонимом parseInt('n', 23). :)

В любом случае, отличный вопрос!

33 голосов
/ 24 июня 2011
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 будет подстрокой S, состоящей из всех символов перед первым таким символом; в противном случае пусть Z будет S.

, которая гарантирует, что целочисленные литералы в стиле C, такие как 15L, будут правильно анализироваться, так что вышеупомянутое эквивалентно

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

"n" - это 23-я буква из списка цифр выше.

1025 * что и требовалось доказать *

16 голосов
/ 24 июня 2011

Я думаю, null преобразуется в строку "null".Таким образом, n на самом деле 23 в 'base24' (то же самое в 'base25' +), u недопустимо в 'base24', поэтому остальная часть строки null будет игнорироваться.Вот почему он выводит 23, пока u не станет действительным в base31.

7 голосов
/ 24 июня 2011

parseInt использует буквенно-цифровое представление, тогда в base-24 «n» допустимо, но «u» является недопустимым символом, тогда parseInt только анализирует значение «n» ....

parseInt("n",24) -> 23

какНапример, попробуйте следующее:

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

Результат будет "3".

...