我如何解决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中没有"8""9" ,函数返回零。 喜欢与否, 这是devise 。

什么是解决方法?

注意:为了完整性,我即将发布一个解决scheme,但这是一个我讨厌的解决scheme,所以请发布其他/更好的答案。


更新:

JavaScript标准( ECMA-262 )的第5版引入了一个突破性的改变,消除了这种行为。 Mozilla有很好的写法 。

这是一个简单的解决scheme常见的Javascript陷阱:

只需指定基地或“基数”,如下所示:

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

你也可以使用Number :

 Number('08'); // 8 

首先,在大多数情况下,你确实不需要parseInt() 。 它的algorithm充满了各种各样的怪癖, 规范中甚至禁止使用0前缀(“函数parseInt的规范不再允许实现将string以0字符开头的string视为八进制值”),但是这需要一段时间改变浏览器行为 (即使我确信没有人在parseInt()有意使用八进制)。 并且Internet Explorer 6将永远不会改变(然而,Internet Explorer 9却不再支持parseInt()八进制)。 它使用的algorithm通常比你想要的要多。 在某些情况下,这是个坏主意。

  1. 如果第一个参数还没有被转换成string。
  2. 修剪数字,所以' 4'变成'4'
  3. 检查string是否以-+开头,并删除此字符。 如果是-使输出为负。
  4. 将基数转换为整数。
  5. 如果基数是0NaN尝试猜测基数。 这意味着寻找(不区分大小写) 0x和(非标准) 0 。 如果找不到前缀,则使用10 (这是最有可能的)。
  6. 如果基数如果存在,则从头开始是160x
  7. find不在基数范围内的第一个字符。
  8. 如果在不在基数范围内的第一个字符之前什么也没有,请返回NaN
  9. 将数字转换为小数点,直到不在范围内的第一个字符。

    例如, parseInt('012z', 27)给出0 * Math.pow(27, 2) + 1 * Math.pow(27, 1) + 2 * Math.pow(27, 0) parseInt('012z', 27) 0 * Math.pow(27, 2) + 1 * Math.pow(27, 1) + 2 * Math.pow(27, 0) parseInt('012z', 27) 0 * Math.pow(27, 2) + 1 * Math.pow(27, 1) + 2 * Math.pow(27, 0)

该algorithm本身并不是很快,但性能各不相同(优化造成的奇迹)。 我已经对JSPerf进行了testing,结果很有趣。 +~~是最快的,除了Chrome,其中parseFloat()以某种方式快于其他选项(比其他选项快2到5倍,其中+实际上慢5倍)。 在Firefox中, ~~testing非常快 – 在某些情况下,我有Infinity循环。

另一件事是正确的。 parseInt()~~parseFloat()使错误无声。 如果parseInt()parseFloat()字符在无效字符后被忽略,那么可以将其称为一个function(在大多数情况下,它对我来说是反function的,就像switch语句一样),如果需要它, 。 如果是~~意味着返回0 ,所以要小心。

在某些情况下, parseInt()可能会伤害到你。 厉害。 例如,如果数字太大以至于写成指数表示法。 然后使用Math方法。

 parseInt(2e30); // will return 2 

无论如何,最后我想列出一个方法来将string转换为数字(整数和浮点数)。 他们有各种用法,你可能会感兴趣什么方法来使用。 在大多数情况下,最简单的是+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使用上述algorithm将参数转换为数字。 避免在大整数上使用它,因为在parseInt(2e30)等情况下,它可能会产生不正确的结果。 此外,永远不要把它作为Array.prototype.map或Underscore.js变体的参数,因为你可能得到奇怪的结果(尝试['1', '2', '3'].map(parseInt)如果你想(为了解释,用console.logreplaceparseInt ))。

在以下情况下使用它:

  1. 当您需要读取以不同基数写入的数据时。
  2. 您需要忽略错误(例如将123px更改为123

否则,使用其他更安全的方法(如果您需要整数,请改用Math.floor )。

+number

+前缀( +number )将数字转换为浮点数。 如果发生错误,则返回NaN ,您可以通过isNaN()或者仅仅通过number !== number (只能为NaN返回true )来比较。 Opera的速度非常快。

除非你想要其他types的特定function,否则使用它。

~~number

~~是一个在整数上使用~两次的hack。 由于只能对整数进行按位操作,因此该数字会自动转换。 大多数浏览器都针对这种情况进行了优化。 由于按位运算只能在Math.pow(2, 32)不要使用大数字的方法。 它在SpiderMonkey引擎上非常快速。

在以下情况下使用它:

  1. 您正在编写代码,其中性能对SpiderMonkey非常重要(如FireFox插件),您不需要检测错误。
  2. 您需要整数和小心产生的JavaScript大小。

parseFloat(number)

parseFloat()工作原理与+一样,只有一个例外 – 它处理数字直到第一个无效字符,而不是返回NaN 。 在V8中速度非常快(但不像Firefox上的速度快)。 与parseInt变体不同, Array.prototype.map应该是安全的。

在以下情况下使用它:

  1. 您正在为Node.js编写性能至关重要的代码,或者您正在编写Google Chrome插件(V8)。
  2. 您需要忽略错误(例如,将42.13px更改为42.13

Number(number)

躲开它。 它的作用就像+前缀,通常比较慢。 唯一有用的地方是Array.prototype.mapcallback – 你不能使用+作为callback。

new Number(number)

当你需要混淆每个人为0的真值和具有'number'时使用它。 真的,不要。

math方法,如Math.ceil(number)

在需要整数时使用它们,因为它比parseInt()更安全,不会忽略意外的字符。 请注意,从技术上讲,它涉及到很长的转换 – string→浮点→整数→浮点数(JavaScript中的数字是浮点数) – 但大多数浏览器都有优化,所以通常不会那么明显。 Array.prototype.map也是安全的。

如果你知道你的值是在32位的有符号整数范围内,那么~~x将在所有情况下做正确的事情。

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

如果你查找二进制不是( ~ ),规范需要一个“ToInt32”转换的参数做了明显的转换到一个Int32,并指定强制NaN值为零。

是的,这是令人难以置信的黑客,但是非常方便

从parseInt文档中 ,使用可选的基数参数来指定base-10:

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

这使我感到迂腐,混乱,冗长(真的,在每一个parseInt中额外的参数?),所以我希望有一个更好的方法。

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

编辑:做你自己的function,做你真正想要的,只是一个选项,如果你不喜欢把parseInt()调用添加“,10”。 它有一个不规范的function的缺点:如果你使用它很方便,但也许更容易混淆别人。

指定基地:

 var number = parseInt(s, 10); 

如果没有第二个参数,用一个假定为十进制的版本replaceparseInt会非常​​顽皮吗? (注 – 未经testing)

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

小数点如何?

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

如果你已经用parseInt做了一堆编码,并且不想在所有内容中添加“,10”,那么你可以重写这个函数来使得base 10成为默认值:

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

这可能会让后来的读者感到困惑,所以使用parseInt10()函数可能会更加明了。 就我个人而言,我更喜欢使用一个简单的函数,而不是一直添加“,10” – 只是为错误创造更多的机会。

您也可以不使用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链接

一元加运算符在它的操作数之前,并且计算它的操作数,但是试图把它转换成一个数字(如果它还没有的话)。 尽pipe一元否定( – )也可以转换非数字,但一元加数是将某些东西转换为数字的最快和首选方式 ,因为它不会对数字执行任何其他操作。