0

During a debugging session, after changing the return of a method from 0 to null, I started seeing an exception that wasn't occurring before.

Digging deeper, I realized that if a variable is holding a Number, you can call a property on it like if it was any other object; the same thing, however, doesn't happen if you try to call a property on the number directly.

For instance:

const number = 0;
console.log(number.foo) // undefined

0.foo // throws SyntaxError: Invalid or unexpected token

What causes this distinction, and where can I read more about it?

UPDATE

I just realized that (0).foo returns undefined as well, so there's at least some consistency there, but I didn't know Numbers could have properties in javascript.

Actually you can only try to read, but never assign to these properties:

const number = 3;
number.someProperty = true;
number.someProperty // returns `undefined`
sandre89
  • 5,218
  • 2
  • 43
  • 64
  • [Number](https://developer.mozilla.org/enUS/docs/Web/JavaScript/Reference/Global_Objects/Number) is a primitive wrapper object. [This SO thread](https://stackoverflow.com/a/9380107/463206) explains the double-dot quirk – radarbob Oct 12 '21 at 17:58
  • Are you asking why `0.foo` and `number.foo` have different outcomes, or are you asking why you can read properties off of numbers? For the first one, see [Why can't I access a property of an integer with a single dot?](/q/9380077/4642212), for the second see [How does primitive types in Javascript have methods and Properties?](/q/53149349/4642212) and [Why can't I add properties to a string object in javascript?](/q/5201138/4642212). – Sebastian Simon Oct 12 '21 at 18:04

3 Answers3

2

. is taken as the optional decimal point directly after a number. Adding a space, using brackets, or adding another . to be the decimal point (so that the second . will be interpreted as a property accessor) will resolve the error.

You can access properties on primitive numbers because attempting to invoke methods or lookup properties on primitives will cause them to be wrapped in the corresponding object to perform the operation.

console.log(0 .foo);
console.log(0..foo);
console.log((0).foo);
Unmitigated
  • 76,500
  • 11
  • 62
  • 80
  • Ok, but why do numbers are acting as objects holding properties? – sandre89 Oct 12 '21 at 17:38
  • I believe numbers *are* objects. Pretty much everything in js is an object – MrMythical Oct 12 '21 at 17:39
  • @sandre89 "In contexts where a method is to be invoked on a primitive or a property lookup occurs, JavaScript will automatically wrap the string primitive and call the method or perform the property lookup." – Unmitigated Oct 12 '21 at 17:39
  • 2
    @MrMythical No, that’s a common misconception. Numbers are primitives. The property access operators `.` and `[`…`]` _coerce_ their reference to an object. That is, `number.foo` is implicitly `Object(number).foo`. In JavaScript, everything is a primitive except Objects. – Sebastian Simon Oct 12 '21 at 17:45
0

0.foo throws SyntaxError: Invalid or unexpected token and the code is not execute because it is not a valid program.

The lexical parser finds 0 and knows that a number starts there. After 0 there is a . that tells it that the number is a real number, not an integer number. If after . there are more digits then they are the decimal part of the number.

In this case there aren't more digits after . and the number is 2.. The next characters after . (foo) are not part of the number but they are an identifier.

All in all, the input "program" 0.foo is parsed into two tokens: 2., which is a number followed by foo, which is an identifier.

This combination (number followed immediately by identifier) is not a valid program fragment. In a correct program, a number can be followed by a space, an operator, a semicolon, a closed parenthesis and probably several other lexical tokens but not by an identifier.

The code fragment is not a valid JavaScript program; it cannot be compiled and therefore it is not executed.

There are several ways to make it work. For example (0).foo.

If you try to run 0.foo in the developers console in Firefox it throws an error that summarizes what I explained above: Uncaught SyntaxError: identifier starts immediately after numeric literal

axiac
  • 68,258
  • 9
  • 99
  • 134
0

This is because of JS OOP nature and its prototyping model. Everything, even primitive values, has a prototype, which is usually an Object. So any primitive, String, Boolean, BigInt, etc. has properties, with exception of null and undefined.

(1).toString()
(1n).toString()
(true).toString()

You can access values' prototypes like this:

Number.prototype
Boolean.prototype
String.prototype

Thus you can add properties to primitives (but you should not do this due to compatibility issues and error-prone nature of such modifications). But here is an example of this:

Number.prototype.negative = function () {
  return Number(this * -1)
};

(1).negative(); // -1

In the case of numbers you got a syntax error because of parsing rules of JS, not because of its OOP model. JS interprets 0. as a number with mantissa, thus it expects a number after the dot.

Paul Rumkin
  • 6,737
  • 2
  • 25
  • 35