Является ли... foo оператором или синтаксисом?

Я слышал, что ... упоминается как "синтаксис распространения" и "оператор распространения", причем последний является гораздо более популярным. URL-адрес соответствующей документации MDN предполагает, что он первоначально назывался оператором распространения, но позже был изменен на синтаксис распространения, а Список операторов MDN не упоминает об этом.

Google, похоже, предлагает, чтобы оператор был более популярен и принимался с такими сайтами, как документация Microsoft и es6-features.org, ссылаясь на него как таковое.

Какой термин будет наиболее правильным в контексте ECMAScript, если таковой имеется, и почему? Что относительно назначения деструкции массива?

Ответ 1

Это не оператор.

Во всех смыслах слова это не одно. Это было огромное заблуждение, поскольку оно было введено и, несмотря на популярное мнение, - это не одно, и есть несколько объективных моментов:

  • Это не соответствует определению оператора
  • Он не может использоваться как оператор
  • Спецификация языка подразумевает, что это не оператор

Следует отметить, что синтаксис распространения происходит в разных "вкусах", используемых в разных контекстах и ​​обычно упоминаемых разными именами при использовании одного и того же акцента. Синтаксис Spread в основном является зонтичным термином для применения символа ... и см. Felix Kling отличный ответ с подробным описанием всех видов использования и имен. Дополнительное объяснение об использовании этих лиц приведено в дополнительном ответе.

Что такое оператор?

Семантически, в контексте ECMAScript, операторы просто встроены в функции, которые принимают аргументы и оценивают одно значение - записываются в префикс, инфикс или постфиксную нотацию и обычно с символическими именами, такими как + или /. Из Wikipedia:

Просто выражение с участием оператора оценивается каким-то образом, и результирующее значение может быть просто значением (r-значение) или может быть объектом, позволяющим присваивать (l-значение).

Например, оператор + приводит к значению, например 2, которое представляет собой правое выражение, а оператор . приводит к объекту, позволяющему присвоить, например, foo.bar выражение стороны.

На поверхности, ... пунктуатор 1 выглядит префиксом унарного оператора:

const baz = [foo, ...bar];

Но проблема с этим аргументом заключается в том, что ...bar не оценивает сингулярное значение; он разворачивает итерационные элементы bar, один за другим. То же самое относится к аргументам распространения:

foo(...bar);

Здесь foo получает отдельные аргументы от итерабельного bar. Они представляют собой отдельные значения, передаваемые в foo, а не только одно значение. Он не соответствует определению оператора, поэтому он не один.

Почему оператор не работает?

Еще один момент, который нужно сделать, заключается в том, что операторы должны быть автономными и возвращать одно значение. Например:

const bar = [...foo];

Как уже упоминалось, это хорошо работает. Проблема возникает при попытке сделать это:

const bar = ...foo;

Если синтаксис распространения был оператором, последний работал бы отлично, потому что операторы оценивали выражение для одного значения, но спрэд не так терпит неудачу. Спрэды синтаксиса и распространения аргументов работают только в контексте массивов и вызовов функций, потому что эти структуры получают несколько значений, предоставляемых путем распространения элементов массива или аргументов. Оценка нескольких значений выходит за рамки того, что может сделать оператор.

Что говорят стандарты?

Полный список операторов приведен в пп. 12.5 - 12.15 в ECMAScript 2015 Language Specification, спецификация, в которой ..., не говоря уже о .... Можно также предположить, что это не оператор. Два основных случая, упомянутых в этом ответе, в которых синтаксис распространения находится в производстве, для вызовов функций (спред аргументов) или литералы массива (синтаксис распространения) описаны ниже:

ArrayLiteral :
  [ Elisionopt ]
  [ ElementList ]
  [ ElementList , Elisionopt ]

ElementList :
  Elisionopt AssignmentExpression
  Elisionopt SpreadElement
  ElementList , Elisionopt AssignmentExpression
  ElementList , Elisionopt SpreadElement

Elision :
  ,
  Elision ,

SpreadElement :
  ... AssignmentExpression
  

И для вызовов функций:

CallExpression :
  MemberExpression Arguments

Arguments :
  ( )
  ( ArgumentList )

ArgumentList :
  AssignmentExpression
  ... AssignmentExpression
  ArgumentList , AssignmentExpression
  ArgumentList , ... AssignmentExpression
  

В этих постановках есть вывод, который можно сделать: что оператор "распространения" не существует. Как упоминалось ранее, операторы должны быть автономными, как в const bar = ...foo, и оценивать одно значение. Синтаксис языка предотвращает это, что означает, что синтаксис распространения никогда не должен был быть автономным. Это расширение для инициализаторов массивов и вызовов функций, расширение их грамматики.

Зачем распространять синтаксис?

Синтаксис, определяемый Wikipedia:

В информатике синтаксис языка компьютера представляет собой набор правил, который определяет комбинации символов, которые считаются правильно структурированным документом или фрагментом на этом языке.

Синтаксис - это, в основном, "форма" языка, правила, которые регулируют законность или нет в отношении того, как должен выглядеть код, и как писать код. В этом случае грамматика ECMAScript специально определяет прецитера ... только для вызова функций и литералов массива в качестве расширения - это правило, которое определяет комбинацию символов (...foo), которые считаются законными вместе, таким образом, это синтаксис, аналогичный тому, как функция стрелки (=>) не является оператором, а синтаксисом 2.

Вызов ... оператор является неправильным. Оператор - встроенная функция, которая принимает аргументы (операнды) и имеет форму префикса, инфикса или постфиксной нотации и оценивает ровно одно значение. ..., удовлетворяя первым двум условиям, не удовлетворяет последним. ..., вместо этого, является синтаксисом, потому что он определен конкретно и явно в грамматике языка. Таким образом, "оператор распространения" объективно более корректно называют "синтаксисом распространения".


1 Термин "пунктуатор" относится к пунктуаторам в ECMAScript 2015 и более поздним спецификациям. Эти символы включают в себя синтаксические компоненты и операторы и являются точками языка. ... - это сам признак, но термин "синтаксис распространения" относится ко всему применению пунктуатора.

2=> сам является пунктуатором точно так же, как ..., но я имею в виду конкретно - это синтаксис функции стрелки, применение => punctuator ((…) => { … }), так же, как синтаксис распространения относится к приложению тега ....

Ответ 2

Другие применения синтаксиса

Существуют другие многочисленные применения синтаксиса распространения/отдыха, не описанные в главном ответе. К ним относятся:

  • Синтаксис останова в параметрах функции
  • Массив и объект 1 назначение деструктуризации
  • Синтаксис распространения объектов в объектных литералах 1

Синтаксис останова

Использование синтаксиса распространения, обычно называемого синтаксисом отдыха, используется для переменного количества аргументов в аргументах функции. Это отличается от аргументов распространения, используемых для передачи аргументов вызову функции на основе итерируемых элементов. Например:

function add(...addends) {
  …
}

Здесь синтаксис остатка используется для функции add для получения остальных аргументов в идентификаторе addends. Кажется, что это оценивается как сингулярное значение, так как addends является массивом переданных аргументов, но что, если мы попытались:

function foo(...[bar, baz]) {
  …
}

Здесь bar и baz будут назначаться значение, соответствующее переданному первому и второму аргументам, поэтому это не всегда оценивается одним значением. Основная проблема заключается в том, что ...addends в первом примере и ...[bar, baz] во втором фактически не оценивает значение вообще - оно просто используется во время операции присвоения массива аргументов идентификатору. Таким образом, это синтаксис, позволяющий переменному числу аргументов функции, а не оператору.

Назначение деструктурирования

Синтаксис спреда также может использоваться во время назначения деструкции массива и на самом деле упоминается как элемент отдыха в спецификации языка (поскольку при использовании в деструкции, он получает остальную часть деструктурированного итерабельного). Убедительный аргумент может быть сделан, поскольку это выглядит как оператор:

const [...bar] = [1, 2, 3];

Он используется как префикс унарного оператора. Здесь bar оценивается как [1, 2, 3] - это одно значение. Но это не всегда происходит, например:

const [first, ...[second, third]] = [1, 2, 3];

Здесь first, second и third оцениваются соответственно 1, 2 и 3. Но ...[second, third] присваивает два идентификатора, а не один, и не оценивает сингулярное значение, а два. Так же, как синтаксис отдыха, основная проблема заключается в том, что ...bar в первом примере и ...[second, third] во втором фактически не оценивает значение вообще - оно просто используется во время операции присваивания. Таким образом, он не является оператором вообще 2 просто новый sytax, способствующий распаковке значений.

Синтаксис распространения объекта

Окончательное использование синтаксиса распространения в объектных литералах, обычно называемых "свойствами распространения объекта", в которых собственные перечислимые свойства целевого объекта распространяются на другие, например:

const foo = { ...bar };

Это не оператор, так же как синтаксис распространения массива не является оператором. Концепция одинаков, а не индексы и элементы в массивах, bar перечислимые ключи и значения распространяются на foo. Здесь набор свойств bar распространяется, а не только одно значение, поэтому оно не соответствует определению оператора.


1Свойства объекта/свойства объекта в настоящее время находятся в предложении Stage 3 для ECMAScript и, скорее всего, будет добавлено в ближайшее время

2 Еще одна проблема с назначением деструктуризации, являющимся оператором, помимо семантики, заключается в том, что спецификация языка определяет это как дополнительный синтаксис, а не дополнительный оператор, и это справедливо. Это не автономно, поскольку это не работает:

const ...bar = [1, 2, 3, 4];

Его контекстуальный, только разрешенный внутри, язык грамматики, литералы объектов и литералы массива, которые являются левыми выражениями. Его также грамматика, которая уточняет интерпретацию левого выражения. Опять же, это расширение для добавления нового синтаксиса к языку, уточнение существующей грамматики. Это подтверждает аргумент со спецификацией.