parseInt против унарного плюса - когда использовать который

Каковы различия между этой линией:

var a = parseInt("1", 10); // a === 1

и эта строка

var a = +"1"; // a === 1

Этот тест jsperf показывает, что унарный оператор намного быстрее в текущей версии Chrome, при условии, что это для node.js!?

Если я пытаюсь преобразовать строки, которые не являются числами, оба возвращают NaN:

var b = parseInt("test" 10); // b === NaN
var b = +"test"; // b === NaN

Итак, когда я должен использовать parseInt над унарным плюсом (особенно в node.js)???

редактировать: и в чем разница с оператором двойной тильды ~~?

6 ответов

Решение

Пожалуйста, смотрите этот ответ для более полного набора случаев




Ну, вот несколько отличий, о которых я знаю:

  • Пустая строка "" оценивает 0, в то время как parseInt оценивает это NaN, ИМО, пустая строка должна быть NaN,

    +'' === 0;              //true
    isNaN(parseInt('',10)); //true
    
  • Одинарный + действует больше как parseFloat так как он также принимает десятичные дроби.

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

    +'2.3' === 2.3;           //true
    parseInt('2.3',10) === 2; //true
    
  • parseInt а также parseFloat разбирает и строит строку слева направо. Если они видят недопустимый символ, он возвращает то, что было проанализировано (если есть) как число, и NaN если ни один не был разобран как число.

    Одинарный + с другой стороны вернется NaN если вся строка не конвертируется в число.

    parseInt('2a',10) === 2; //true
    parseFloat('2a') === 2;  //true
    isNan(+'2a');            //true
    
  • Как видно из комментария Alex K.., parseInt а также parseFloat будет разбирать по характеру. Это означает, что шестнадцатеричные и экспонентные обозначения потерпят неудачу, так как x а также e обрабатываются как нечисловые компоненты (по крайней мере, на основе 10).

    Одинарный + будет конвертировать их должным образом, хотя.

    parseInt('2e3',10) === 2;  //true. This is supposed to be 2000
    +'2e3' === 2000;           //true. This one's correct.
    
    parseInt("0xf", 10) === 0; //true. This is supposed to be 15
    +'0xf' === 15;             //true. This one's correct.
    

Конечная таблица преобразования числа в число:

EXPRS = [
    'parseInt(x)',
    'parseFloat(x)',
    'Number(x)',
    '+x',
    '~~x',
    'x>>>0',
    'isNaN(x)'

];

VALUES = [
    '"123"',
    '"+123"',
    '"-123"',
    '"123.45"',
    '"-123.45"',
    '"12e5"',
    '"12e-5"',
    
    '"0123"',
    '"0000123"',
    '"0b111"',
    '"0o10"',
    '"0xBABE"',
    
    '"4294967295"',
    '"123456789012345678"',
    '"12e999"',

    '""',
    '"123foo"',
    '"123.45foo"',
    '"  123   "',
    '"foo"',
    '"12e"',
    '"0b567"',
    '"0o999"',
    '"0xFUZZ"',

    '"+0"',
    '"-0"',
    '"Infinity"',
    '"+Infinity"',
    '"-Infinity"',

    'null',
    '[].undef',
    'true',
    'false',
    'Infinity',
    'NaN',

    '{}',
    '{valueOf: function(){return 42}}',
    '{toString: function(){return "56"}}',

];

//////

function wrap(tag, s) {
    if (s && s.join)
        s = s.join('');
    return '<' + tag + '>' + String(s) + '</' + tag + '>';
}

function table(head, rows) {
    return wrap('table', [
        wrap('thead', tr(head)),
        wrap('tbody', rows.map(tr))
    ]);
}

function tr(row) {
    return wrap('tr', row.map(function (s) {
        return wrap('td', s)
    }));
}

function val(n) {
    return n === true || Number.isNaN(n) ? wrap('b', n) : String(n);
}

var rows = VALUES.map(function (v) {
    var x = eval('(' + v + ')');
    return [v].concat(EXPRS.map(function (e) {
        return val(eval(e))
    }));
});

document.body.innerHTML = table(["x"].concat(EXPRS), rows);
table { border-collapse: collapse }
tr:nth-child(odd) { background: #fafafa }
td { border: 1px solid #e0e0e0; padding: 5px; font: 12px monospace }
td:not(:first-child) { text-align: right }
thead td { background: #3663AE; color: white }
b { color: red }

Я полагаю, что таблица в ответе thg435 является исчерпывающей, но мы можем подвести итог следующим шаблонам:

  • Унарный плюс не относится ко всем ложным значениям одинаково, но все они оказываются ложными.
  • Унарный плюс отправляет true до 1, но "true" в NaN,
  • С другой стороны, parseInt является более либеральным для строк, которые не являются чистыми цифрами. parseInt('123abc') === 123, в то время как + отчеты NaN,
  • Number будет принимать действительные десятичные числа, тогда как parseInt просто отбрасывает все после десятичного числа. таким образом parseInt имитирует поведение C, но, возможно, не идеально подходит для оценки пользовательского ввода.
  • Оба обрезают пробелы в строках.
  • parseInt, будучи плохо спроектированным парсером, принимает восьмеричный и шестнадцатеричный ввод. Унарный плюс принимает только шестнадцатеричный.

Ложные значения конвертируются в Number после того, что будет иметь смысл в C: null а также false оба равны нулю. "" переход к 0 не совсем соответствует этому соглашению, но имеет для меня достаточно смысла.

Поэтому я думаю, что если вы проверяете пользовательский ввод, унарный плюс имеет правильное поведение для всего, кроме того, что он принимает десятичные дроби (но в моих реальных случаях меня больше интересует перехват ввода электронной почты вместо userId, значение опускается полностью и т. Д.), Тогда как parseInt слишком либерален.

Будьте осторожны, parseInt быстрее, чем + унарный оператор в Node.JS, неверно, что + или |0 быстрее, они быстрее только для элементов NaN.

Проверь это:

var arg=process.argv[2];

rpt=20000;
mrc=1000;

a=[];
b=1024*1024*1024*1024;
for (var i=0;i<rpt;i++)
 a[i]=Math.floor(Math.random()*b)+' ';

t0=Date.now();
if ((arg==1)||(arg===undefined))
 for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
  c=a[i]-0;
 }
t1=Date.now();
if ((arg==2)||(arg===undefined)) {
 for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
  d=a[i]|0;
 }
}
t2=Date.now();
if ((arg==3)||(arg===undefined)) {
 for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
  e=parseInt(a[i]);
 }
}
t3=Date.now();
 if ((arg==3)||(arg===undefined)) {
 for (var j=0;j<mrc;j++) for (var i=0;i<rpt;i++) {
  f=+a[i];
 }
}
t4=Date.now();

console.log(a[i-1],c,d,e,f);
console.log('Eseguiti: '+rpt*mrc+' cicli');
console.log('parseInt '+(t3-t2));
console.log('|0 '+(t2-t1));
console.log('-0 '+(t1-t0));
console.log('+ '+(t4-t3));

Я рекомендую использовать Math.floor (или ~~, если вы знаете, что числа положительные) вместо parseString. +(выражение) выходит за рамки, потому что +(выражение) больше похоже на parseFloat. Посмотрите этот небольшой тест:

      // 1000000 iterations each one
node test_speed
Testing ~~, time: 5 ms
Testing parseInt with number, time: 25 ms
Testing parseInt with string, time: 386 ms
Testing Math.floor, time: 18 ms

Исходный код теста:

      
/* el propósito de este script es evaluar
que expresiones se ejecutan más rápido para así 
decidir cuál usar */

main()
async function main(){
    let time, x 
    let number = 23456.23457
    
    let test1 = ()=>{
        x = 0
        time = Date.now() 
        for(let i=0;i<1000000;i++){
            let op = Math.floor(number / 3600)
            x = op
        }
        console.info("Testing Math.floor, time:", Date.now() - time, "ms")
    }

    let test2 = ()=>{
        x = 0
        time = Date.now() 
        for(let i=0;i<1000000;i++){
            let op = parseInt(number / 3600)
            x = op
        }
        console.info("Testing parseInt with number, time:", Date.now() - time, "ms")
    }

    let test3 = ()=>{
        x = 0
        time = Date.now() 
        for(let i=0;i<1000000;i++){
            let op = parseInt((number / 3600).toString())
            x = op
        }
        console.info("Testing parseInt with string, time:", Date.now() - time, "ms")
    }

    let test4 = ()=>{
        x = 0
        time = Date.now() 
        for(let i=0;i<1000000;i++){
            let op = ~~(number / 3600)
            x = op
        }
        console.info("Testing ~~, time:", Date.now() - time, "ms")
    }
    
    test4()
    test2()
    test3()
    test1()
    
}

Учитывайте производительность тоже. Я был удивлен, что parseInt превосходит унарный плюс на iOS:) Это полезно для веб-приложений только с высокой загрузкой процессора. В качестве практического правила я бы предложил JS opt-guys рассматривать любого оператора JS вместо другого с точки зрения производительности мобильных устройств в настоящее время.

Итак, иди мобильным первым;)

Другие вопросы по тегам