views:

148

answers:

5

Take a look at the following code:

Number.prototype.isIn = function () {
 for (var i = 0, j = arguments.length; i < j; ++i) {
     if (parseInt(this, 10) === arguments[i]) {
      return true;
     }
 }
 return false;
};

var x = 2;
console.log(x.isIn(1,2,3,4,5)); // <= 'true'
console.log(2.isIn(1,2,3,4,5)); // <= Error: 'missing ) after argument list'

Why is it that when it's a variable, the code works correctly yet when it is a number literal, it fails ?


And also, strangely enough, why does the following line work?

console.log((2).isIn(1,2,3,4,5)); // <= 'true'

In the above line, I basically enclosed the literal in parenthesis.

+11  A: 

It's a syntax error because you are representing a number. Strings can work that way, but not numbers, because a period immediately following a number symbolizes a decimal value. The character after the . is causing the error.

Josh Stodola
A: 

My understanding goes that numbers are literals, and not an objects. However, when you define a variable as an number, then it becomes a new Number() object.

So doing the following;

var x = 10;

Is the same as going;

var x = new Number(10);

As for the second example; I can only assume that putting the brackets around the number has made the JavaScript compiler assume that the value is an anonymous Number() object. Which makes sense I guess...

Cammy Wan Kenobi
`x = 10` and `x = new Number(10)` are not equivalent.
Crescent Fresh
Correct: `alert(typeof 10);` vs. `alert(typeof new Number(10));`
Josh Stodola
Thanks for the correction guys.
Cammy Wan Kenobi
A: 

Although the distinction is often not apparent because of automatic type conversion, JavaScript supports a number of primitive types as well as objects:

var foo = 10;
var bar = new Number(10);
alert(foo.toString(16)); // foo is automatically wrapped in an object of type Number 
                         // and that object's toString method is invoked
alert(bar.toString(16)); // bar is already an object of type Number, 
                         // so no type conversion is necessary before 
                         // invoking its toString method
var foo2 = "foo";
var bar2 = new String("foo");
alert(typeof foo2);      // "string" - note the lowercase "s", not a String object
alert(typeof bar2);      // "object"

alert(typeof true)       // "boolean"
alert(typeof new Boolean(true)) // "object"

and something that really confuses the issue:

// the next line will alert "truthy"
alert("Boolean object with value 'false'" + (new Boolean(false) ? " is truthy" : " is falsy"));
// the next line will alert "falsy"
alert("boolean primitive with value 'false'" + (false ? " is truthy" : " is falsy"));

Although relying on automatic type conversion makes life a little easier, one should always have at the back of one's mind a good understanding of what types your primitive values and objects actually are; a surprisingly large number of JS bugs come about because of people failing to realise that, for example, something which looks like a number is actually a string, or that some operation they have carried out has resulted in something that used to contain a number having been assigned a new value that is a string, or an object.

EDIT: to more closely address the original question, which I now realise I hadn't answered explicitly: 10.foo() will cause a syntax error as the . is seen as a decimal point, and foo() isn't a valid sequence of characters to be parsed as a number. (10).foo() will work as the enclosing brackets (10) make the entire construct before the . into a single expression. This expression is evaluated and returns the primitive number value 10. Then the . is seen as treating that primitive value in an object context, so it is automatically wrapped in an object of type Number (this is the automatic type conversion in action). Then the foo property of that object, found on its prototype chain, is referenced; and the final () cause that property to be treated as a function reference and invoked in the 'this' context of the Number object that was wrapped around the primitive value at the point of encountering the ..

NickFitz
`10` and `Number(10)` are identical. `bar = Number(10)` does not make `bar` an "object of type Number". Perhaps you meant to compare `10` with `new Number(10)`? OR `'10'` vs `Number('10')`?
Crescent Fresh
Oops, good catch - `new` was missing. I should copy this stuff from the console where I do my tests rather than retyping it :-)
NickFitz
+2  A: 

Josh was correct, but you do not have to use a variable to use a method of a number, although it is usually more convenient to do so.

5.isIn(1,2,3,4,5) returns an error

5.0.isIn(1.2.3.4.5) returns true, as does
(5).isIn(1,2,3,4,5)
kennebec
`5..isIn` and `5 .isIn` also remove the [parsing] ambiguity.
Crescent Fresh
+5  A: 

Most of the answers already stated that a dot after a numeric literal is considered part of this number as a fraction separator. But if you still want to use the dot as an operator then the quick and easy fix would be leaving an empty space between the number and the space.

2 .isIn(1,2,3,4,5) // <- notice the space between 2 and .
Andris
+1 hmm, this is an interesting use of whitespace!
Andreas Grech
actually it's quite logical - the dot is a standard operator in JavaScript just like + or = or -. You can write something like 4 + 5 so you can use 4 . isIn() as well.
Andris