2014-09-24 3 views
27

сегодня я наткнулся на странный (на мой взгляд) случай в JavaScript. Я передал не шестнадцатеричную строку в функцию parseInt с базой 16 и ... Я получил результат. Я бы ожидал, что функция выкинет какое-то исключение или, по крайней мере, вернет NaN, но ему удалось разобрать его и вернуть int.Почему parseInt ('dsff66', 16) возвращает 13?

Мой вызов был:

var parsed = parseInt('dsff66', 16); // note the 's' in the first argument 
 
document.write(parsed);

и результат был: 13.

Я заметил, что он «останавливает» синтаксический анализ с первым символом, который не относится к системе цифр, указанной во втором аргументе, поэтому вызывая parseInt('fg',16), я получил бы 15.

На мой взгляд, он должен вернуть NaN. Может ли кто-нибудь объяснить мне, почему это не так? Зачем кому-то нужно, чтобы эта функция вела себя так (верните целое число, даже если это не точное представление переданной строки)?

+8

Обратите внимание, что если вы делаете что-то вроде 'ParseInt ("2foo", 10) 'вы получаете' 2'. Это так, как ожидалось. Он анализирует до первого нечислового символа –

+35

* На мой взгляд, он должен вернуть NaN. Может кто-нибудь объяснить мне, почему это не так? * Потому что спецификация говорит, что это не должно. И, к сожалению, мое мнение или ваше не имеет значения для спецификации. –

+14

'Если parseInt встречает символ, который не является цифрой в указанном радиусе, он игнорирует его и все последующие символы и возвращает целочисленное значение, обработанное до этой точки. Источник: https://developer.mozilla.org/en- US/docs/Web/JavaScript/Reference/Global_Objects/parseInt –

ответ

22

Зачем кому-то эта функция ведет себя как это (возвращают целое число, даже если оно не является точным представление переданной строки)?

Поскольку большую часть времени (на сегодняшний день) вы работаете с базовыми 10 номеров, и в этом случае JS может просто отливать - не разобрать - строку в число. (edit: По-видимому, не только base-10, см. обновление ниже.)

Поскольку JS динамически типизирован, некоторые строки работают отлично, как цифры без какой-либо работы с вашей стороны. Например:

"21"/3; // => 7 
"12.4"/4; // => 3.1 

Нет необходимости parseInt там, потому что "21" и "12.4" существу номера уже. Если, однако, строка была "12.4xyz", тогда вы действительно получили бы NaN при делении, так как это явно не число и не может быть неявно брошено или принуждено к одному.

Вы также можете явно «отличить» строку до номера с помощью Number(someString). Хотя он также поддерживает только базу 10, , он действительно вернет NaN для недопустимых строк.

Так как JS уже имеет неявный и явный тип casting/conversion/coercion, то роль parseInt не должна быть еще одной функцией кастинга другого типа.

parseInt роль будет, а, парсинг функция. Функция, которая изо всех сил пытается понять ее вклад, возвращая все, что может. Для вас, когда у вас есть строка, вы не можете просто бросить, потому что это не совсем отлично. (И, как основной синтаксис JS, он напоминает C, так как ответ apsillers объясняется красиво.)

И поскольку это синтаксический анализатор, а не функция кастинга, у него есть дополнительная функция, позволяющая обрабатывать другие базы, чем 10 .

Теперь, вы можете спросить, почему не строгая литья функции, которая не основание 10 числа, и будете жаловаться, как вы хотите, но ... эй, там просто нет. Дизайнеры JS просто решили, что parseInt хватит, потому что, опять же, 0x63 процента времени, вы имеете дело с базой 10.

Ближайший вы можете получить на «кастинг», вероятно, что-то ужасно Hacky как:

var hexString = "dsff66"; 
var number = eval("0x" + hexString); // attempt to interpret as a hexadecimal literal 

which'll бросить SyntaxError, потому что 0xdsff66 не является допустимым шестигранной буквальным.

Update: Как Lekensteyn указывает в комментариях, появляется JS правильно отливать 0x -prefixed строки шестнадцатеричных тоже. Я не знал этого, но на самом деле это, кажется, работает:

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); 
} 
+5

Ew, 'eval'. Посредством «отливки» строки к числу вы можете получить аналогичный эффект. '1 *" 0xdsff66 "дает' NaN' по крайней мере в Firefox. Проверка того, подтверждает ли это спецификация, является упражнением для читателя. – Lekensteyn

+0

@Lekensteyn Да, ew. Я сказал, что это ужасно :) Но интересно, что строка '0x'-prefixed также отлита! Не знал этого - отлично работает здесь и в Chrome. – Flambino

+0

Ну, это приятное объяснение :) Спасибо вам обоим. –

43

parseInt считывает ввод до тех пор, пока не встретит недопустимый символ, а затем использует любой действительный ввод, который он прочитал до этого недопустимого символа. Рассмотрим:

parseInt("17days", 10); 

Это будет использовать вход 17 и опустить все после того, как инвалиду d.

От ECMAScript specification:

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

В вашем примере, s является недопустимым основанием 16 символов, поэтому parseInt использует только ведущий d.

Что касается почему такого поведения было включено: нет никакого способа, чтобы знать наверняка, но это вполне вероятно, попытка воспроизвести поведение strtol (строки в длинный) из стандартной библиотеки языка C. Из strtol(3) man page:

... строка преобразуются в длинное значение INT очевидным образом, остановки на первый символ, который не является действительной цифрой в данной базе.

Это соединение дополнительно поддерживается (до некоторой степени) тем, что оба parseInt и strtol указаны игнорировать ведущие пробелы, и оба они могут принять на себя ведущую 0x для шестнадцатеричных значений.

+0

+1 для указания спецификаций. – Robert

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.После этого он перестает разбираться, так как следующий символ не является шестнадцатеричным, следовательно, результатом.

12

В данном конкретном случае parseInt() интерпретировать письмо от "A" к "F" как hexadecimal и разбора тех десятичных чисел. Это означает, что d вернется 13.

Что ParseInt() делает

  • parseInt("string", radix) интерпретировать цифры и буквы в строке в виде шестнадцатеричной (это зависит от системы счисления) на номер.

  • parseInt() только номер или буква синтаксического разбора как шестнадцатеричный с начала строки до тех пор, пока символ недействителен как шестнадцатеричный.

  • Если parseInt() не может найти любое число или букву как шестнадцатеричное в начале строки parseInt() будет возвращать NaN.

  • Если радиус не определен, радиус равен 10.

  • Если строка начинается с "0x", то основанием является 16.

  • Если установленный радиус 0, то основанием является 10.

  • Если радикс равен 1, parseInt() return NaN.

  • Если радикс является 2, parseInt() только разобрать "0" и "1".

  • Если радикс является 3, ParseInt() только разобрать "0", "1" и "2". И так далее.

  • parseInt() синтаксический анализ "0" к 0, если есть число не следует за ним, как результат и удалить 0, если есть число следует за ним. например «0» возврат 0 и «01» Возвращение 1.

  • Если радикс является 11, parseInt() только разобрать строка, которая начинается с числом от "0" до "9" и/или букв "A".

  • Если радикс является 12, ParseInt только разобрать строку, которая начинается с числом от "0" до "9" и/или букв "A" и "B", и так далее.

  • максимальная радикс является 36, он будет разбирать строку, начинающуюся с номером от "0" до "9" и/или письма от "A" к "Z".

  • Если символы, интерпретируемые как шестнадцатеричные более одного, каждый символ будет иметь другое значение, хотя эти символы являются одним и тем же символом. например parseInt("AA", 11) первое "A" имеет другое значение со вторым "A".

  • Различные radix будут возвращать другое число, хотя строки являются одной и той же строкой.

в действии

document.body.innerHTML = "<b>What parseInt() does</b><br>" + 
 
          "parseInt('9') = " + parseInt('9') + "<br>" + 
 
          "parseInt('0129ABZ', 0) = " + parseInt('0129ABZ', 0) + "<br>" + 
 
          "parseInt('0', 1) = " + parseInt('0', 1) + "<br>" + 
 
          "parseInt('0', 2) = " + parseInt('0', 2) + "<br>" + 
 
          "parseInt('10', 2) = " + parseInt('10', 2) + "<br>" + 
 
          "parseInt('01', 2) = " + parseInt('01', 2) + "<br>" + 
 
          "parseInt('1', 2) = " + parseInt('1', 2) + "<br>" + 
 
          "parseInt('A', 10) = " + parseInt('A', 10) + "<br>" + 
 
          "parseInt('A', 11) = " + parseInt('A', 11) + "<br>" + 
 
          "parseInt('Z', 36) = " + parseInt('Z', 36) + "<br><br>" + 
 
          "<b>The value:</b><br>" + 
 
          "parseInt('A', 11) = " + parseInt('A', 11) + "<br>" + 
 
          "parseInt('A', 12) = " + parseInt('A', 12) + "<br>" + 
 
          "parseInt('A', 13) = " + parseInt('A', 13) + "<br>" + 
 
          "parseInt('AA', 11) = " + parseInt('AA', 11) + " = 100 + 20" + "<br>" + 
 
          "parseInt('AA', 12) = " + parseInt('AA', 12) + " = 100 + 30" + "<br>" + 
 
          "parseInt('AA', 13) = " + parseInt('AA', 13) + " = 100 + 40" + "<br>" + 
 
          "parseInt('AAA', 11) = " + parseInt('AAA', 11) + " = 1000 + 300 + 30" + "<br>" + 
 
          "parseInt('AAA', 12) = " + parseInt('AAA', 12) + " = 1000 + 500 + 70" + "<br>" + 
 
          "parseInt('AAA', 13) = " + parseInt('AAA', 13) + " = 1000 + 700 + 130" + "<br>" + 
 
          "parseInt('AAA', 14) = " + parseInt('AAA', 14) + " = 1000 + 900 + 210" + "<br>" + 
 
          "parseInt('AAA', 15) = " + parseInt('AAA', 15) + " = 1000 + 1100 + 310";

+1

'Если радикс не определен, радиус равен 10.' - хотя это _generally_ true, более старые браузеры могут по умолчанию использовать значение счисления 8. См. [ParseInt at MDN] (https://developer.mozilla.org/en- США/документы/Web/JavaScript/Справка/Global_Objects/ParseInt). –

Смежные вопросы