-4

What's the secret behind (weird) number conversion in JavaScript?

I've manage to figure out some using isnumeric function on couple of test cases presented below.

//
function isnumeric(n) {
  try {
    return (isFinite(n = n.valueOf()) || ((1 / 0 === Math.abs(n)) && (n + n === n)))
      && eval(n) === parseFloat(Number(n));
  } catch (b) {}
  return false;
}

console.clear();
[
.1, 1, 0, "0", Infinity, 1/0, -1/0, +null, 01, 037, 1 + [], 
Math.pow([], 2), 0xff, 0x12ef, +[], -[], -1/[], []/Infinity, 
"0x12", 1/null, null/1, Math.sqrt([]), []/1, Infinity/[], new Date, 
Math.E, "1.2e-12", 0.1e11, null + +[], Math.sqrt(null), 1/"\t\r\n ",
" \t\r\n\uFEFF\xA0\x0B\u2028\u2029\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000"/1
].every(isnumeric);
//
// true
// 

isFinite(n = n.valueOf()) deals with numeric-like-values(strings, numbers, dates),

(1 / 0 === Math.abs(n)) && (n + n === n) filters out Infinity from (string) "Infinity",

eval(n) === parseFloat(Number(n)) part handles string-numerics holding characters: "x, e" etc.

Thanks!

sudocdslash
  • 43
  • 1
  • 5

1 Answers1

4

Since I don't really know what you're asking, I'll just answer everything.

n.valueOf

(spec, mdn)

This call is done in case a number object (and not a number primitive) was passed to the function:

var numObj = new Number(4),
    numPrim = 4;

console.log(typeof numObj, typeof numPrim); //object, number
numObj === numPrim; //false
//however:
numObj.valueOf() === numPrim; //true

isFinite

(spec, mdn)

isFinite(arg) converts arg to a number (I'll touch on how later), and checks if it's not one of NaN, +∞, or −∞:

isFinite(4); //true
isFinite(Infinity); //false
isFinite("Infinity"); //false (the argument is converted into a number)
isFinite("foobar"); //false (converting a random string to a number yields NaN

(1 / 0 === Math.abs(n))

1/0 brings us +∞, so this checks if the argument is +∞ or -∞. Infinity was caught in the isFinite call (notice how strange this is: isFinite was used as a trimming factor...but we still want Infinity to be a number).

(n + n === n)

Again, this catches Infinity, as it's the only value to satisfy this equation (didn't check, but it makes sense; I'll talk about it in the end). Pretty useless, really.

eval(n) === parseFloat(Number(n))

(parseFloat: spec, mdn) (Number: spec, mdn).

This is an odd bunny. The reasoning behind it is beyond me. The left-hand side executes n as if it was js, and the right-hand converts n to a number. Twice. What this hopes to achieve is to probably catch NaN or an invalid number string.

It belongs in the Department of Redundancy Department for several reasons:

  1. Number calls the internal ToNumber function, which converts the argument to a number based on js' syntax. Thus, if the argument was an invalid number, the return value would've been NaN. eval would've done the same thing at the end, but with greater overhead.

  2. The outer call to parseFloat is ridiculous. parseFloat converts its argument to a string, and then tries to see if the string begins in a valid number. This was already done in Number, which is also more strict than parseFloat (as the former demands that the entire string be a valid number, not just the beginning).

  3. ToNumber was already called in isFinite. If the argument was either NaN or a string which isn't a number, we would've gotten false.

So this function contains unneeded duplication and some illogical calls. Taken from this great answer, it could be written as:

function isNumeric (n) {
  return !isNaN(parseFloat(n)) && isFinite(n);
}

On ToNumber

When you do 4 * [], what happens (section 11.5) is that each side of the operator is taken, converted to a number, and then the answer is calculated. How are they converted to a number? That's in section 9.3, the ToNumber function. In most cases where you see strange behaviour, it's because you use an object as one of the values to be operated on, and that object is converted into a string (see this question for some examples).

Community
  • 1
  • 1
Zirak
  • 38,920
  • 13
  • 81
  • 92