В соответствии с предложением @dlatikay, следуя существующей догадке, исследуйте в CoveredParenthesizedExpression
дал лучше понять, что здесь происходит.
Видимо, причина, по которой нетерминал нельзя найти в spec, чтобы объяснить, почему (foo)
приемлемо в качестве LeftHandExpression
, на удивление просто. Я предполагаю, что вы понимаете, как работают синтаксические анализаторы, и что они работают в два отдельных этапа: лексирование и парсинг.
Что я узнал из этого небольшого исследования, так это то, что конструкция (foo)
технически не доставляется парсеру и, следовательно, движку, как вы могли бы подумать.
var foo = (((bar)));
Как мы все знаем, что-то подобное совершенно законно. Почему? Что ж, визуально вы можете просто игнорировать круглые скобки, в то время как оператор сохраняет смысл.
Точно так же вот еще один допустимый пример, даже с точки зрения удобочитаемости, потому что скобки только поясняют, что PEMDAS уже делает неявным.
(3 + ((4 * 5) / 2)) === 3 + 4 * 5 / 2
>> true
Из этого можно сделать вывод об одном ключевом наблюдении, учитывая то, как уже работают синтаксические анализаторы. (помните, Javascript все еще анализируется (читай: компилируется) и затем запустить) Таким образом, в прямом смысле эти скобки констатируют очевидное.
Итак, все, что было сказано, что именно происходит?
По сути, круглые скобки (за исключением параметров функции) свернуты в соответствующие группы содержащих их символов. IANAL, но с точки зрения непрофессионала это означает, что круглые скобки интерпретируются только для того, чтобы указать синтаксическому анализатору, как сгруппировать то, что он читает. Если контекст скобок уже в порядке и, таким образом, не требует какой-либо настройки выдаваемого AST, то (машинный) код генерируется так, как будто этих круглых скобок вообще не существует.
Анализатор более или менее ленив, предполагая, что скобки неуместны. (что в данном случае неверно)
Хорошо, а где именно это происходит?
Согласно 12.2.1.5 Статический Семантика: IsValidSimpleAssignmentTarget в спецификации,
PrimaryExpression: (CoverParenthesizedExpressionAndArrowParameterList)
- Пусть expr будет CoveredParenthesizedExpression of CoverParenthesizedExpressionAndArrowParameterList.
- Возвратите IsValidSimpleAssignmentTarget выражения.
т.е. Если ожидается primaryExpression
, верните все, что находится внутри скобок, и используйте это.
Из-за этого в этом сценарии он не преобразует (foo)
в CoveredParenthesizedExpression{ inner: "foo" }
, а просто преобразует его в foo
, что сохраняет тот факт, что это Identifier
и, следовательно, синтаксически, хотя и не обязательно лексически, допустимо.
TL;DR
Это то, что нужно.
Хотите узнать больше?
Ознакомьтесь с ответом @Bergi.
person
TERMtm
schedule
14.05.2017
(foo) === foo
, также у нас есть(true ? foo : bar) === foo;
(оба правильные). - person skobaljic   schedule 15.05.2017foo = (5)
, например. - person Egor Stambakio   schedule 15.05.2017(foo) === foo
потому что значения равны. Впрочем, и там ссылка не имеет значения.(foo)
в идеальном мире вернул бы содержащийся объект, а не ссылкуfoo
, которая заменяется выражением=
. - person TERMtm   schedule 15.05.2017CoverParenthesizedExpressionAndArrowParameterList
квалифицируется какIsValidSimpleAssignmentTarget
, что дает его внутреннююexpr
. - person Cee McSharpface   schedule 15.05.2017(foo)
в этом случае не является оператором группировки, точно так же, как вызов метода(obj.foo)()
не имеет оператора группировки вокругobj.foo
.(0, obj.foo)()
, напротив, содержит оператор группировки. Предыдущий вызов передаетobj
в качестве контекстаthis
вfoo
(точно так же, какobj.foo()
), последний вызов - нет. См. Почему оператор запятой изменяетthis
в вызове функции. - person Sebastian Simon   schedule 23.03.2021