Каковы правила автоматической установки точки с запятой JavaScript (ASI)?

Ну, сначала я, вероятно, должен спросить, зависит ли это от браузера.

Я читал, что если найден недействительный токен, но раздел кода действителен до этого недопустимого токена, точка с запятой вставлена ​​перед токеном, если ему предшествует разрыв строки.

Однако общий пример, приведенный для ошибок, вызванных вводом с запятой, следующий:

return
  _a+b;

.. который, похоже, не следует этому правилу, так как _a будет действительным токеном.

С другой стороны, разрыв цепочек вызовов работает как ожидалось:

$('#myButton')
  .click(function(){alert("Hello!")});

Есть ли у кого-нибудь более подробное описание правил?

Ответ 1

Прежде всего, вы должны знать, на какие утверждения влияет автоматическая вставка точки с запятой (также известная как ASI для краткости):

  • пустой оператор
  • инструкция var
  • выражение выражения
  • выражение do-while
  • continue выражение
  • оператор break
  • return statement
  • throw выражение

Конкретные правила ASI описаны в спецификации §11.9.1. Правила автоматической вставки точки с запятой

Описаны три случая:

  1. Когда встречается токен (LineTerminator или }), который не разрешен грамматикой, точка с запятой вставлена перед ним, если:

    • Маркер отделен от предыдущего токена, по крайней мере, одним LineTerminator.
    • Токен }

    например:

    { 1
    2 } 3
    

    трансформируется в

    { 1
    ;2 ;} 3;
    

    NumericLiteral 1 соответствует первому условию, следующий токен - это терминатор линии.
    2 соответствует второму условию, следующий токен }.

  2. Когда встречается конец входного потока токенов, и синтаксический анализатор не может проанализировать поток входных токенов как одну полную программу, то точка с запятой автоматически вставлена в конец входного потока.

    например:

    a = b
    ++c
    

    преобразуется в:

    a = b;
    ++c;
    
  3. Этот случай возникает, когда токен разрешается некоторым произведением грамматики, но производство является ограниченным производством, точка с запятой автоматически вставлена перед ограниченным токеном.

    Ограниченные постановки:

    UpdateExpression :
        LeftHandSideExpression [no LineTerminator here] ++
        LeftHandSideExpression [no LineTerminator here] --
    
    ContinueStatement :
        continue ;
        continue [no LineTerminator here] LabelIdentifier ;
    
    BreakStatement :
        break ;
        break [no LineTerminator here] LabelIdentifier ;
    
    ReturnStatement :
        return ;
        return [no LineTerminator here] Expression ;
    
    ThrowStatement :
        throw [no LineTerminator here] Expression ; 
    
    ArrowFunction :
        ArrowParameters [no LineTerminator here] => ConciseBody
    
    YieldExpression :
        yield [no LineTerminator here] * AssignmentExpression
        yield [no LineTerminator here] AssignmentExpression
    

    Классический пример с ReturnStatement:

    return 
      "something";
    

    трансформируется в

    return;
      "something";
    

Ответ 2

Прямо от ECMA-262, пятое издание Спецификация ECMAScript:

7.9.1 Правила автоматической вставки точки с запятой

Существует три основных правила вставки точки с запятой:

  • Когда, когда программа анализируется слева направо, появляется токен (называемый оскорбительным токеном), который не разрешен никаким производством грамматики, затем точка с запятой автоматически вставлена ​​перед оскорбительным токеном, если один или несколько из следующих условий:
    • Оскорбительный токен отделен от предыдущего токена хотя бы одним LineTerminator.
    • Оскорбительный токен }.
  • Когда, когда программа анализируется слева направо, встречается конец входного потока токенов, и синтаксический анализатор не может проанализировать поток входных токенов как один полный ECMAScript Program, тогда точка с запятой автоматически вставленный в конец входного потока.
  • Когда, когда программа анализируется слева направо, встречается токен, допускаемый некоторой производством грамматики, но производство является ограниченным производством, а токен будет первым токеном для терминала или нетерминала сразу после аннотации " [no LineTerminator здесь]" в рамках ограниченного производства (и поэтому такой токен называется ограниченным токеном), а ограниченный токен отделен от предыдущего токена хотя бы одним LineTerminator, то точка с запятой автоматически вставлена ​​перед ограниченным токеном.

Тем не менее, существует дополнительное условие переопределения для предыдущих правил: точка с запятой никогда не вставлена ​​автоматически, если точка с запятой затем анализируется как пустой оператор или если эта точка с запятой станет одной из двух точек с запятой в заголовке a for (см. 12.6.3).

Ответ 3

Я не мог понять эти 3 правила в спецификациях слишком хорошо - надеюсь, что у вас будет более простой английский, но вот что я собрал из JavaScript: The Definitive Guide, 6th Edition, David Flanagan, O'Reilly, 2011:

Цитата:

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

Другая цитата: для кода

var a
a
=
3 console.log(a)

JavaScript не рассматривает второй разрыв строки как точку с запятой, потому что он может продолжить синтаксический анализ более длинного оператора a = 3;

и

два исключения из общего правила, что JavaScript интерпретирует разрывы строк как точки с запятой, когда он не может проанализировать вторую строку как продолжение оператора в первой строке. Первое исключение включает выражения return, break и continue

... Если после любого из этих слов появляется разрыв строки... JavaScript всегда будет интерпретировать разрыв строки как точку с запятой.

... Второе исключение включает в себя операторы ++ и -... Если вы хотите использовать любой из этих операторов в качестве постфиксных операторов, они должны отображаться в той же строке, к которой они применяются. В противном случае разрыв строки будет рассматриваться как точка с запятой, а ++ или - будет обрабатываться как префиксный оператор, применяемый к следующему коду. Рассмотрим этот код, например:

x 
++ 
y

Он анализируется как x; ++y;, а не как x++; y

Поэтому я думаю, что для упрощения это означает:

В целом, JavaScript будет рассматривать его как продолжение кода, если это имеет смысл, за исключением двух случаев: (1) после некоторых ключевых слов, таких как return, break, continue и ( 2) если он видит ++ или -- в новой строке, то он добавит ; в конце предыдущей строки.

Часть о "относиться к нему как к продолжению кода до тех пор, пока это имеет смысл" заставляет его чувствовать, как регулярное выражение жадного соответствия.

С учетом сказанного это означает, что для return с разрывом строки интерпретатор JavaScript будет вставлять ;

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

и по этой причине классический пример

return
{ 
  foo: 1
}

не будет работать должным образом, потому что интерпретатор JavaScript будет рассматривать его как:

return;   // returning nothing
{
  foo: 1
}

После return не должно быть разрыва строки:

return { 
  foo: 1
}

чтобы он работал правильно. И вы можете вставить ; самостоятельно, если вы будете следовать правилу использования ; после любого оператора:

return { 
  foo: 1
};

Ответ 4

Что касается вставки точки с запятой и оператора var, остерегайтесь забывать запятую при использовании var, но охватывая несколько строк. Кто-то нашел это в моем коде вчера:

    var srcRecords = src.records
        srcIds = [];

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

Ответ 5

Самое контекстное описание автоматической вставки точек с запятой в JavaScript, которое я нашел, взято из книги о крафт-интерпретаторах.

Правило "автоматической вставки точек с запятой" в JavaScripts является нечетным. Если в других языках предполагается, что большинство новых строк имеют смысл, а в многострочных операторах следует игнорировать лишь несколько, JS предполагает обратное. Он обрабатывает все ваши переводы строк как бессмысленные пробелы, если только он не сталкивается с ошибкой разбора. Если это так, он возвращается и пытается превратить предыдущий символ новой строки в точку с запятой, чтобы получить что-то грамматически правильное.

Он продолжает описывать это так, как вы бы кодировали запах.

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