parseInt (null, 24) === 23 ... подождите, что?

226

Хорошо, поэтому я возился с 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


ТЛ; др

Объясните, почему parseInt(null, 24) === 23это верное утверждение.

Роберт
источник
49
Как странно. JavaScript всегда держит вас в напряжении.
FishBasketGordo
1
Точка данных: alert(parseInt(null, 34) === 23)произведеноfalse
Стивен П
1
alert(parseInt(null,26)===23);тоже выдает истину?!?!
Петр Иванов
6
[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745,[37...]:NaN
zzzzBov
1
В качестве дополнительного примечания, так undefinedкак первый параметр возвращает странные результаты за 30-е годы
zzzzBov,

Ответы:

240

Это преобразование 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
Игнасио Васкес-Абрамс
источник
3
@ Томалак: Кто сказал, что он использует toString()?
Игнасио Васкес-Абрамс
3
@Ignacio. На самом деле я был не прав. Я не понимал, что 37 имел в виду радикс. Извини за это.
Майк Сэмюэль
3
@ Роберт, нет, я растерялся и подумал, что он требует чего-то другого, кроме того, что он требовал. Это правильный ответ. Извиняюсь всем вокруг.
Майк Сэмюэль
19
Я все еще думаю, что этот ответ мог бы сделать с некоторыми ссылками. Хотя это совершенно правильно, на самом деле это всего лишь одно большое утверждение ...
Гонки Легкости на Орбите
4
@ Томалак - проверь мой ответ по всем ссылкам. Этот ответ является правильным (и первым), поэтому я думаю, что он должен остаться принятым. Хотя никогда не повредит объяснить, что происходит под капотом;)
Дэвид Титаренко
118

Мозилла говорит нам :

Функция 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.)

Гонки легкости на орбите
источник
2
Это очень трагическое поведение parseInt (я думал, почему он не был предназначен для исключения, хотя в этом случае). Я бы предпочел использовать NUMBER () вместо этого, когда смогу.
Грижеш Чаухан
79

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

От 15.1.2.2 parseInt (string , radix):

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

  • Пусть inputString будет ToString (string).
  • Пусть 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, используя буквы AZ и az для цифр со значениями от 10 до 35. (Однако, если R равно 10 и Z содержит более 20 значащих цифр, каждая значащая цифра после 20-го числа может быть заменена цифрой 0 по выбору реализации, и если R не равен 2, 4, 8, 10, 16 или 32, тогда mathInt может быть зависимым от реализации приближением математического целого числа значение, которое представлено Z в обозначении radix-R.)
  • Пусть number будет числовым значением для mathInt.
  • Вернуть знак × номер.

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

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

введите описание изображения здесь

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

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

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

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

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

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

Дэвид Титаренко
источник
33
parseInt( null, 24 ) === 23

Эквивалентно

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

что эквивалентно

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

Цифры для основания 24: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, а, b, c, d, e, f, ..., n.

Спецификация языка говорит

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

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

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

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

QED

Майк Самуэль
источник
16

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

Floern
источник
7

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

parseInt("n",24) -> 23

в качестве примера попробуйте это:

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

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

fdaines
источник