Ответ 1
Ответы Greg Hewgill и icktoofay верны во всех отношениях, однако я бы хотел немного опуститься, абстрагироваться: давайте посмотрим, что на самом деле происходит в соответствии с спецификацией javascript.
Раздел 7.8.3 спецификации определяет числовые литералы. Мы можем видеть следующее:
DecimalLiteral ::
DecimalIntegerLiteral . DecimalDigits(opt) ExponentPart(opt)
. DecimalDigits ExponentPart(opt)
DecimalIntegerLiteral ExponentPart(opt)
DecimalIntegerLiteral ::
0
NonZeroDigit DecimalDigits(opt)
A DecimalLiteral
, число - это группа десятичных цифр, возможно, за которой следует точка, за которой, возможно, следуют другие цифры (за которыми может следовать, например, показатель степени e12
). Другими словами, 42.
является законным и равен 42
, а 3e2
равен 300
.
Обратите внимание, что если у нас есть точка, мы либо ожидаем, что за ней последует большее количество цифр/экспонентов, или за ней ничего не будет. Однако, и это важная часть, точка является частью числа. Помните об этом, когда мы перейдем к рассмотрению того, как обрабатывается оператор-точка, obj.prop
.
Раздел 11.2.1, Аксессоры свойств описывает нотацию точек и скобок для доступа к члену:
MemberExpression . IdentifierName
CallExpression
предназначен для вызовов функций, которые нам не нужны. Обратите внимание на то, что мы ожидаем MemberExpression
(который может быть DecimalLiteral
- но не верьте мне на слово, посмотрите и убедитесь, что я прав).
Увидите эту маленькую точку? Логично перепрыгнуть вперед и сказать "хорошо, там точка в схеме здесь... и там точка в 4.foo
... так почему же ошибка?" Увы, мой гипотетический друг, которого я использую для этих предложений, вы забыли, как выглядит DecimalLiteral
! Перейдем к двум примерам и посмотрим, что произойдет.
42.foo
^
Каретка представляет символ, в котором мы находимся. До сих пор мы находимся внутри DecimalLiteral / DecimalIntegerLiteral / NonZeroDigit
(это довольно много). Перейдите к следующему символу:
42.foo
^
Все еще часть номера, совершенно правильная DecimalDigit
.
42.foo
^
ok, поэтому мы вышли из части DecimalIntegerLiteral
. Здесь та же диаграмма на схеме:
DecimalIntegerLiteral . DecimalDigits(opt) ExponentPart(opt)
^
Итак, мы находимся на точке, которая является вполне действительной частью числа. Теперь мы потребляем его, как часть числа, и двигаемся дальше:
42.foo
^
f
не является ни частью DecimalDigits
, ни ExponentPart
, теперь мы отстаем от числа. И что теперь? Что это за f
? Это не часть ничего. Может быть, это свойство доступа? Посмотрим на схему:
MemberExpression . IdentifierName
^
Мы определенно на MemberExpression
, но у нас нет точки, которая следует за ней, - эта точка уже является частью числа. Мы достигли синтаксической ошибки: мы прекращаем выполнение и бросаем ее. Надеюсь, вы не живете в стеклянном доме.
Надеюсь, теперь вы поймете, почему работает 42..foo
. Когда мы выйдем из MemberExpression
, мы столкнемся с другой точкой:
42..foo
^
MemberExpression . IdentifierName
^
Далее следует совершенно законный IdentifierName
.
Конечно, есть несколько других способов отделить точку от числа. Один из способов, как вы показали, состоит в том, чтобы окружать буквальный круглые скобки: (42).foo
. Когда мы достигли конца круглых скобок, мы вышли из MemberExpression
и на точку. Другой способ - вставить пробел: 42 .foo
, так как пространство не может быть частью числа, и оно нейтрально для синтаксического анализатора, поэтому оно не вызывает ошибки.