Это синтаксис распространения или оператор распространения?

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

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

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


person Community    schedule 05.07.2017    source источник


Ответы (2)


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

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

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

Следует отметить, что расширенный синтаксис бывает разных «разновидностей», используется в разных контекстах и ​​обычно упоминается под разными именами при использовании одного и того же знака препинания. Синтаксис распространения — это, по сути, общий термин для применения знака препинания ..., и см. отличный ответ Феликса Клинга, подробно описывающий все употребления и названия. Дополнительные пояснения об использовании этих лиц приведены в дополнительном ответе.

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

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

Проще говоря, выражение, включающее оператор, оценивается каким-то образом, и результирующее значение может быть просто значением (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., спецификация, в которой представлен ..., но не упоминается .... Также можно сделать вывод, что это не оператор. Два основных случая, упомянутых в этом ответе, в которых синтаксис распространения находится в производстве, для вызовов функций (аргументы расширения) или литералы массива (синтаксис расширения) описаны ниже:

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, и оценивать одно единственное значение. Синтаксис языка предотвращает это, а это означает, что синтаксис распространения никогда не предназначался для автономного использования. Это расширение инициализаторов массивов и вызовов функций, расширение их грамматики.

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

Синтаксис, определенный в Википедии:

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

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

Называть ... оператором неправильно. Оператор – это встроенная функция, которая принимает аргументы (операнды) в виде префикса, инфикса или постфикса, и возвращает ровно одно значение. ..., удовлетворяя первым двум условиям, не удовлетворяет последнему. ..., напротив, является синтаксисом, поскольку он конкретно и явно определен в грамматике языка. Таким образом, «оператор распространения» объективно правильнее называть «синтаксисом расширения».


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

2 => сам по себе является знаком препинания, как и ..., но я имею в виду синтаксис функции стрелки, применение знака препинания => ((…) => { … }), так же как синтаксис расширения относится к применению знака препинания ....

person Community    schedule 05.07.2017
comment
Проголосовал за исследование. Все еще задаетесь вопросом, почему это важно? Если в просторечии он уже известен как оператор спреда, я сомневаюсь, что кто-то неправильно поймет, что имеется в виду в разговоре. - person Patrick Roberts; 05.07.2017
comment
@PatrickRoberts Вы правы, но я хотел сделать различие. Самая большая проблема для меня заключалась в том, что не было ни одного авторитетного поста, который дал бы точный ответ на вопрос, и вопрос возник из-за того, почему у MDN был синтаксис, а у всех остальных был оператор. Я просто хотел поделиться своими знаниями, но также показать разницу. - person Andrew Li; 05.07.2017
comment
@PatrickRoberts Допустим, человек собирается сделать карьеру грамматического наци JS, но все еще не знает, должен ли он / она наказывать людей, которые говорят «оператор спреда». Этот ответ дает очень хорошее объяснение. - person Estus Flask; 05.07.2017
comment
Для меня ... — это знак препинания, который используется в синтаксисе распространения и остальных параметрах, это не оператор и не синтаксис сам по себе. Это эквивалентно другим знакам препинания, таким как ,, ; и :, которые используются в именованных частях грамматики (списки параметров, операторы, литералы объектов), но не называются операторами. Кроме того, последняя версия спецификации, я думаю, ECMAScript 2017 вы упомянули 2015 год, поскольку именно здесь был введен .... Наконец, в §12.5–12.6 упоминаются все операторы, и ... не является одним из них. Извините, могу дать вам только один голос за ваши усилия. ;-) - person RobG; 06.07.2017
comment
@RobG Да, я процитировал 6-е издание, потому что оно было представлено тогда. Я обязательно отредактирую и упомяну, что все операторы упомянуты, или вы могли бы, если хотите. - person Andrew Li; 06.07.2017
comment
@RobG Я согласен с вашим утверждением о том, что ... является знаком препинания - буквальные три точки сами по себе являются знаком препинания, но фактический способ их применения к инициализаторам массива и остальным параметрам я бы назвал синтаксисом. Просто мои мысли. - person Andrew Li; 06.07.2017
comment
Согласен, просто комментирую. Я бы предпочел, чтобы автор отредактировал статью, иначе она будет довольно грязной. ;-) - person RobG; 06.07.2017
comment
Спасибо за это исследование. - person Ben Aston; 06.07.2017
comment
Знак препинания не удовлетворяет критериям оператора даже при деструктуризации массива. Несмотря на то, что он используется подобно (а не как) унарному оператору, он не приводит к единственному значению. На самом деле выражение ...bar вообще ничего не дает - оно используется в операции присваивания, но не более того. И хотя переменная bar заканчивается одним значением, в этом нет необходимости — операндом может быть любое назначаемое целевое выражение с любым возможным количеством идентификаторов, например в let [first, ...[second, third]] = … или let [...{0: first, length: x}] = …. - person Bergi; 08.07.2017
comment
Да, это может помочь. Или мы просто перемещаем его во второй ответ (чтобы сохранить его на платформе), который повторяет все из На первый взгляд, …, но говорит об отдыхе, а не о синтаксисе распространения. - person Bergi; 08.07.2017
comment
FWIW, не уверен, должно ли это быть в этом ответе или в другом, но, возможно, стоит поощрять людей называть это тем, что зависит от контекста: элемент остатка, параметр остатка, аргумент распространения или элемент распространения (это то, что я пытался объяснить в моем ответе на эту тему). - person Felix Kling; 11.07.2017
comment
@FelixKling На самом деле, вместо того, чтобы заново изобретать колесо с точки зрения ответов, я бы предпочел просто дать ссылку на ваш отличный ответ, а не переписывать тот же контент. - person Andrew Li; 11.07.2017
comment
Меня это тоже устраивает ;) - person Felix Kling; 11.07.2017
comment
Семантически, в контексте ECMAScript, операторы — это просто встроенные функции, которые принимают аргументы и возвращают одно значение. Не совсем так. Тернарный оператор, например. не принимает аргументы, как это делает вызов функции, поскольку происходит короткое замыкание. - person Michael Gummelt; 07.05.2019
comment
@MichaelGummelt Дело не в том, что операторы представляют собой какой-то конкретный вызов функции со списками аргументов, а в том, что операторы представляют собой некоторую абстрактную операцию, указанную в спецификации, которая оценивается как одно значение - акцент на одном значении. - person Andrew Li; 08.05.2019
comment
@ Li357 Li357 Это может быть задумано автором, но это не то, что он написал. - person Michael Gummelt; 10.05.2019
comment
@MichaelGummelt Я автор. Вы можете редактировать по своему усмотрению, если хотите уточнить это. - person Andrew Li; 10.05.2019

Другое использование синтаксиса

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

  • Синтаксис Rest в параметрах функции
  • Назначение деструктурирования массива и объекта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, а просто новый синтаксис, помогающий распаковывать значения.

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

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

const foo = { ...bar };

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


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

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

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

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

person Community    schedule 08.07.2017