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


446

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

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

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

return
  _a+b;

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

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

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

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


22
Там является спецификация ...
Майлз

33
@Miles Только не по твоей неработающей ссылке ;-) ecma-international.org/publications/standards/Ecma-262.htm
Зак Лисобей,

3
Смотрите стр. 26 из приведенных выше PDF.
ᴠɪɴᴄᴇɴᴛ


см. раздел 11.9 «Автоматическая вставка
Эндрю Лам

Ответы:


455

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

  • пустое заявление
  • var заявление
  • выражение выражения
  • do-while заявление
  • continue заявление
  • break заявление
  • return заявление
  • 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";

4
# 1: токен, который не разрешен грамматикой, обычно не является ограничителем строки, не так ли (если вы не имеете в виду ограниченную продукцию из # 3)? Это думаю, что вы должны опустить скобки. # 2 Не должен ли пример показать только вставку после ++cдля ясности?
Берги

3
пожалуйста, обратите внимание, что ASI не нужно фактически «вставлять точки с запятой», просто чтобы завершить оператор в парсере движка ...
Aprillion

1
что говорит «входной поток», это значит «линия»? «Поток входных токенов» усложняет понимание
неполярность

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

пожалуйста, объясните, как, согласно этим правилам, приведенный ниже пример, написанный a 者 無極 而 生 «a [LineBreak] = [LineBreak] 3», все еще работает
Nir O.

45

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

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

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

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

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


44

Я не мог понять эти 3 правила в спецификациях слишком хорошо - надеюсь, что у меня будет что-то более простое на английском языке - но вот что я собрал из JavaScript: Полное руководство, 6-е издание, Дэвид Фланаган, О'Рейли, 2011:

Quote:

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

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

var a
a
=
3 console.log(a)

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

а также:

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

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

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

x 
++ 
y

Разбирается как x; ++y;, а не какx++; y

Я думаю, чтобы упростить это, это означает:

В общем, JavaScript будет рассматривать его как продолжение кода до тех пор , как это имеет смысл - за исключением 2 -х случаях: (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
};

17

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

    var srcRecords = src.records
        srcIds = [];

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


4
вот почему я использую jsLint
Зак Лисобей

1
JsHint / Lint прямо в вашем редакторе кода с немедленным ответом :)
dmi3y

5
@balupton Когда запятая, которая заканчивалась бы строкой, забыта, точка с запятой автоматически вставляется. В отличие от правила, это было больше похоже на «гочу».
Кислород

1
Я думаю, что Balupton является правильным, это разница, если вы напишите: var srcRecords = src.records srcIds = [];в одной строке и забудете запятую, или вы напишите «return a && b» и ничего не забудете ... но разрыв строки перед a вставит автоматическую точку с запятой после return, что определяется правилами ASI ...
Себастьян

3
Я думаю, что четкость ввода var( let, const) в каждой строке превышает долю секунды, которая требуется для ее ввода.
squidbe

5

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

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

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

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


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.