ParseInt vs unary plus,什么时候使用哪个?

这句话有什么不同:

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

还有这条线

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

这个 Jsperf 测试表明,假设一元操作符适用于 node.js,那么在当前 chrome 版本中,一元操作符的速度要快得多?

如果我尝试转换不是数字的字符串,返回 NaN:

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

那么我应该在什么时候使用 parseInt而不是 unary plus (特别是在 node.js 中) ? ? ?

编辑 : 和双波浪操作符 ~~有什么区别?

58686 次浏览

这里有一些我知道的不同之处:

  • 空字符串 ""计算为 0,而 parseInt计算为 NaN。我的意思是,一个空字符串应该是 NaN

      +'' === 0;              //true
    isNaN(parseInt('',10)); //true
    
  • 一元 +的行为更像 parseFloat,因为它也接受小数。

    另一方面,parseInt在看到非数字字符时停止解析,如小数点 .的句点。

      +'2.3' === 2.3;           //true
    parseInt('2.3',10) === 2; //true
    
  • parseIntparseFloat解析并构建字符串 从左到右。如果它们看到一个无效的字符,它将返回被解析为数字的内容(如果有的话) ,如果没有被解析为数字的内容,则返回 NaN

    另一方面,如果整个字符串不可转换为一个数字,则一元 +将返回 NaN

      parseInt('2a',10) === 2; //true
    parseFloat('2a') === 2;  //true
    isNaN(+'2a');            //true
    
  • @ Alex K.的注释所示,parseIntparseFloat将按字符进行解析。这意味着十六进制和指数符号将失败,因为 xe被视为非数值分量(至少在基数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.
    

最终的数字转换表: Conversion table

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"',
'BigInt(1)',


'null',
'undefined',
'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) || n === "Error" ? wrap('b', n) : String(n);
}


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


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 语言中是有意义的: nullfalse都为零。""到0并不完全遵循这个约定,但对我来说足够有意义。

因此,我认为如果你正在验证用户输入,一元加除了接受小数以外,对所有事情都有正确的行为(但在我的现实生活中,我更感兴趣的是捕获电子邮件输入而不是 userId,完全忽略值等) ,而 parseInt 过于自由。

也考虑 表演。我很惊讶 parseInt在 iOS 上击败了 unary plus:)这对于 CPU 消耗很大的 web 应用程序很有帮助。作为一个经验法则,我建议 JS 的选择家伙考虑任何一个 JS 运营商超过另一个从移动性能的角度来看,现在。

所以,去 手机优先;)

注意,parseInt 比 Node.JS 中的 + unary 运算符快, 假设 + 或 | 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。+ (expression)超出了范围,因为 + (expression)更像 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()
    

}