5

Since I started working with JS, I've thought the only way to invoke a function on a number literal is to put it in expression position by wrapping it with parens, like so:

1.toString();
// SyntaxError: identifier starts immediately after numeric literal

(1).toString();
// "1"

Today, it occurred to me to try this:

0.1.toString();
// "0.1"

Why does this work? A pointer into the official spec would be great.

Edit Ambiguity was my first thought, but then decided that there's no ambiguity in 1.toString() either. It's deeper than I first thought, but I still think I'm right. Here's why:

Property names can begin with digits

var obj = { "1" : 1, "2" : 2 };

Property names that begin with digits can only be referenced with square brackets

obj.1;
// SyntaxError: Unexpected token ILLEGAL
obj['1'];
// 1

Also:

1['toString']();
// '1'

Therefore, 1. followed by any non-digit will always be a method call or property access, never a decimal number. Likewise, 1. followed by any digit will always be a decimal number, never a method call or property access.

mwcz
  • 8,949
  • 10
  • 42
  • 63
  • 1
    as a note, you could use `1..toString()`, the first `.` indicates the decimal point, the second indicates usage for the function. – zzzzBov May 02 '12 at 16:29
  • 1
    Related: http://stackoverflow.com/q/2300197 – Christian C. Salvadó May 02 '12 at 16:32
  • Clever, thanks zzzzBov and CMS. – mwcz May 02 '12 at 16:37
  • 1
    Well there's a *small* ambiguity around "e" - is it the start of an exponent, or is it the first letter of an identifier? Consider `1.e10();` - tokenizers don't like having to "back up" :-) – Pointy May 02 '12 at 16:54
  • I don't follow... that would be written `1e10`, no? – mwcz May 02 '12 at 18:31
  • 1
    I don't agree with marking this duplicate. The question contains more context and the chosen answer is higher quality (including a link to the actual JavaScript spec where this behavior is defined). – mwcz May 03 '16 at 17:39

1 Answers1

5

Once it's seen the first . in 0.1, then a subsequent . cannot be part of the number.

It's all about ambiguity.

edit — section 7.8.3 of the spec explicitly insists on this:

The source character immediately following a NumericLiteral must not be an IdentifierStart or DecimalDigit.

I'm not sure exactly what that's trying to prevent, but the JavaScript lexer is pretty gnarly, mostly thanks to the regex literal grammar and the need for a weird parser-lexer hack to deal with that.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Pointy
  • 405,095
  • 59
  • 585
  • 614
  • I don't think there's any ambiguity here. Updating my question with the reasoning. I'm confused by that sentence from the spec... `0.1.toString()` is valid, but looks to me like a NumericLiteral immediately followed by a DecimalDigit. – mwcz May 02 '12 at 16:38
  • @mwcz Yes, I agree that there's no *apparent* ambiguity, but there might be some obscure reason for the insistence that the case be treated as an error. The spec doesn't go into any real detail. And `0.1.toString` is *not* a numeric literal followed by a decimal digit: it's a numeric literal followed by a `.` character, which is neither a decimal digit nor an identifier start. – Pointy May 02 '12 at 16:39
  • Ah, thanks. I looked at `.` and couldn't distinguish it from `.` :) I've added the reasoning against ambiguity, although I'm guessing you've arrived at it independently. – mwcz May 02 '12 at 16:44