Как я могу работать с октальным поведением JavaScript parseInt?

Попробуйте выполнить следующее в JavaScript:

parseInt('01'); //equals 1
parseInt('02'); //equals 2
parseInt('03'); //equals 3
parseInt('04'); //equals 4
parseInt('05'); //equals 5
parseInt('06'); //equals 6
parseInt('07'); //equals 7
parseInt('08'); //equals 0 !!
parseInt('09'); //equals 0 !!

Я только усвоил, что JavaScript считает, что начальный ноль указывает восьмеричное целое число, и поскольку нет "8" или "9" в базе-8 функция возвращает ноль. Нравится вам это или нет, это по дизайну.

Каковы обходные пути?

Примечание. Для полноты я собираюсь опубликовать решение, но это решение, которое я ненавижу, поэтому, пожалуйста, напишите другие/лучшие ответы.


Обновление:

5-е издание стандарта JavaScript (ECMA-262) вводит нарушение, которое устраняет это поведение. Mozilla имеет хорошую запись.

Ответы

Ответ 1

Это обычная версия Javascript с простым решением:

Просто укажите базу или "radix", например:

parseInt('08',10); // 8

Вы также можете использовать Number:

Number('08'); // 8

Ответ 2

Прежде всего, в большинстве случаев вам действительно не нужно parseInt(). Этот алгоритм полон различных причуд, префикс 0 даже запрещен спецификацией ( "спецификация функции parseInt больше не позволяет реализации для обработки строк, начинающихся с символа 0 как восьмеричные значения." ), но потребуется некоторое время, чтобы изменить поведение браузера (даже если я что никто не использует восьмеричные намеренно в parseInt()). И Internet Explorer 6 никогда не изменится (Internet Explorer 9, однако, удалил поддержку восьмеричных в parseInt()). Используемый им алгоритм обычно делает больше, чем вы хотите от него. В некоторых случаях это плохая идея.

  • Первый аргумент преобразуется в строку, если он еще не был.
  • Обрезайте число, поэтому ' 4' становится '4'.
  • Проверьте, начинается ли строка с - или + и удаляется этот символ. Если это было -, выход отрицательный.
  • Преобразовать radix в integer.
  • Если radix 0 или NaN попытайтесь угадать radix. Это означает поиск (без учета регистра) для 0x и (нестандартного) 0. Если префикс не найден, используется 10 (и это то, что вы, скорее всего, что).
  • Если radix является 16 strip 0x с самого начала, если он существует.
  • Найдите первый символ, который не находится в радиусе радиуса.
  • Если нет первого символа, который не находился в радиусе радиуса, верните NaN.
  • Преобразуйте число до десятичного числа до первого символа, который не находится в диапазоне.

    Например, parseInt('012z', 27) дает 0 * Math.pow(27, 2) + 1 * Math.pow(27, 1) + 2 * Math.pow(27, 0).

Сам алгоритм не очень быстрый, но производительность меняется (оптимизация делает чудеса). Я положил тест на JSPerf, и результаты были интересными. + и ~~ являются самыми быстрыми с исключением для Chrome, где parseFloat() как-то быстрее, чем другие параметры (в 2-5 раз быстрее, чем другие параметры, где + на самом деле в 5 раз медленнее). В Firefox тест ~~ выполняется очень быстро - в некоторых случаях у меня есть циклы Infinity.

Другое дело - правильность. parseInt(), ~~ и parseFloat() делают ошибки молчащими. В случае parseInt() и parseFloat() символы игнорируются после недопустимого символа - вы можете назвать это функцией (в большинстве случаев это анти-функция для меня, так же как и инструкции switch), и если вам это нужно, используйте один из тех. В случае ~~ это означает возврат 0, поэтому будьте осторожны.

В некоторых случаях parseInt() может навредить вам. Плохо. Например, если число настолько велико, что оно записано в экспоненциальной нотации. Затем используйте методы Math.

parseInt(2e30); // will return 2

В любом случае, в конце я хочу создать список методов для преобразования строк в числа (как целые, так и плавающие). У них есть различные способы использования, и вам может быть интересно, какой метод использовать. В большинстве случаев самый простой метод +number, используйте его, если можете. Что бы вы ни делали (кроме первого метода), все должны давать правильный результат.

parseInt('08', 10); // 8
+'08';              // 8
~~'08';             // 8
parseFloat('08');   // 8
Number('08');       // 8
new Number('08');   // 8... I meant Object container for 8
Math.ceil('08');    // 8

parseInt(number)

Не использовать. Просто как тот. Либо используйте parseInt(number, 10), либо это обходное решение, которое волшебным образом исправит функцию parseInt. Обратите внимание, что это обходное решение не будет работать в JSLint. Пожалуйста, не жалуйтесь на это.

(function () {
    "use strict";
    var oldParseInt = parseInt;
    // Don't use function parseInt() {}. It will make local variable.
    parseInt = function (number, radix) {
        return oldParseInt(number, radix || 10);
    };
}());

parseInt(number, radix)

parseInt преобразует аргумент в числа, используя упомянутый выше алгоритм. Избегайте использования его на больших целых числах, так как он может делать неправильные результаты в таких случаях, как parseInt(2e30). Кроме того, никогда не давайте в качестве аргумента для изменения Array.prototype.map или Underscore.js, поскольку вы можете получить странные результаты (попробуйте ['1', '2', '3'].map(parseInt), если хотите (для пояснения замените parseInt на console.log)).

Используйте его, если:

  • Когда вам нужно читать данные, написанные в разных версиях.
  • Вам нужно игнорировать ошибки (например, изменить 123px на 123)

В противном случае используйте другие более безопасные методы (если вам нужно целое число, используйте Math.floor).

+number

Префикс

+ (+number) преобразует число в float. В случае ошибки он возвращает NaN, который вы можете сравнить либо с помощью isNaN(), либо просто с помощью number !== number (он должен возвращать true только для NaN). Это очень быстро в Opera.

Используйте его, если вы не хотите использовать специальные функции других типов.

~~number

~~ - это хак, который использует ~ два раза на целое число. Поскольку ~ побитовая операция может выполняться только для целых чисел, число автоматически преобразуется. У большинства браузеров есть оптимизация для этого случая. Поскольку побитовые операции работают только ниже Math.pow(2, 32), никогда не используйте этот метод с большими числами. Он невероятно быстро работает на двигателе SpiderMonkey.

Используйте его, если:

  • Вы пишете код, где производительность важна для SpiderMonkey (например, плагинов FireFox), и вам не требуется обнаружение ошибок.
  • Вам нужно целое число и размер результирующего JavaScript.

parseFloat(number)

parseFloat() работает как + с одним исключением - он обрабатывает номер до первого недопустимого символа вместо возврата NaN. Это очень быстро (но не так быстро, как ~~ в Firefox) в V8. В отличие от изменения parseInt, он должен быть безопасным с Array.prototype.map.

Используйте его, если:

  • Вы пишете критически важный код для Node.js или вы создаете плагины Google Chrome (V8).
  • Вам нужно игнорировать ошибки (например, изменить 42.13px на 42.13)

Number(number)

Избегайте этого. Он работает как префикс + и обычно медленнее. Единственное использование, где это может быть полезно, это обратный вызов для Array.prototype.map - вы не можете использовать + как обратный вызов.

new Number(number)

Используйте его, когда вам нужно путать всех с 0, являющимся истинным значением и имеющим typeof of 'number'. Серьезно, не делайте.

Математические методы, такие как Math.ceil(number)

Используйте их, когда вам нужно целое число, более безопасное, чем parseInt(), не игнорируя неожиданные символы. Обратите внимание, что технически это связано с длинным преобразованием - строка → float → integer → float (числа в JavaScript - это float), но у большинства браузеров есть оптимизация для него, поэтому обычно это не так заметно. Он также безопасен с Array.prototype.map.

Ответ 3

Если вы знаете, ваше значение будет в 32-битном целочисленном диапазоне, то ~~x выполнит правильную работу во всех сценариях.

~~"08" === 8
~~"foobar" === 0
~~(1.99) === 1
~~(-1.99)  === -1

Если вы ищете двоичный файл not (~), для спецификации требуется преобразование "ToInt32" для аргумента, которое делает очевидное преобразование в Int32, и указано для того, чтобы принудить значения NaN к нулю.

Да, это невероятно хаки, но это так удобно...

Ответ 4

Из документации parseInt используйте необязательный аргумент radix для указания base-10:

parseInt('08', 10); //equals 8
parseInt('09', 10); //equals 9

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

Ответ 5

function parseDecimal(s) { return parseInt(s, 10); }

edit: сделать свою собственную функцию, делать то, что вы действительно хотите, просто вариант, если вам не нравится добавлять ", 10" все время к вызову parseInt(). Недостатком является нестандартная функция: более удобна для вас, если вы используете ее много, но, возможно, более запутанную для других.

Ответ 6

Укажите базу:

var number = parseInt(s, 10);

Ответ 7

Было бы очень непослушно заменять parseInt версией, которая принимает десятичное значение, если у нее нет второго параметра? (примечание - не проверено)

parseIntImpl = parseInt
parseInt = function(str, base){return parseIntImpl(str, base ? base : 10)}

Ответ 8

Как насчет этого для десятичного числа:

('09'-0) === 9  // true

('009'-0) === 9 // true

Ответ 9

Вы также можете вместо использования parseFloat или parseInt использовать унарный оператор (+).

+"01"
// => 1

+"02"
// => 2

+"03"
// => 3

+"04"
// => 4

+"05"
// => 5

+"06"
// => 6

+"07"
// => 7

+"08"
// => 8

+"09"
// => 9

и для хорошей меры

+"09.09"
// => 9.09

MDN Link

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

Ответ 10

Если вы уже сделали кучу кодирования уже с parseInt и не хотите добавлять ", 10" ко всему, вы можете просто переопределить эту функцию, чтобы сделать базу 10 по умолчанию:

window._oldParseInt = window.parseInt;
window.parseInt = function(str, rad) {
    if (! rad) {
        return _oldParseInt(str, 10);
    }
    return _oldParseInt(str, rad);
};

Это может смутить более позднего читателя, поэтому создание функции parseInt10() может быть более самоочевидной. Лично я предпочитаю использовать простую функцию, чем добавлять ", 10" все время - просто создает больше возможностей для ошибок.

Ответ 11

Эта проблема не может быть воспроизведена в последних версиях Chrome и Firefox (2019).