Які правила для автоматичної вставки точки з комою в JavaScript (ASI)?


445

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

Я читав, що якщо недійсний маркер знайдено, але розділ коду діє до тих пір, як недійсний маркер, перед токеном вставляється крапка з комою, якщо йому передує розрив рядка.

Однак загальним прикладом цитированних помилок, спричинених вставкою крапки з комою, є:

return
  _a+b;

.. який, мабуть, не дотримується цього правила, оскільки _a був би дійсним маркером.

З іншого боку, розрив ланцюгів викликів працює як очікується:

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

Хтось має більш поглиблений опис правил?



33
@Miles Тільки не за вашим зламаним посиланням ;-) ecma-international.org/publications/standards/Ecma-262.htm
— Zach Lysobey

3
Див. С. 26 вище згаданого PDF.
— ᴠɪɴᴄᴇɴᴛ


див. розділ 11.9 Автоматичне введення крапки з комою
— Ендрю Лам

Відповіді:


454

Перш за все, ви повинні знати, на які твердження впливає автоматична вставка крапки з комою (також відома як 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 не потрібно насправді "вставляти крапки з комою", а лише скасувати заяву в аналізаторі двигуна ...
— Квільйон

1
що там сказано "вхідний потік", це означає "рядок"? "Потік вхідних маркерів" робить його дещо складнішим для розуміння
— неополярність

Чи працює специфікація зв'язку для когось іншого? Це призвело мене до майже порожньої сторінки, на якій було мертве посилання.
— intcreator

поясніть, будь ласка, як, згідно з цими правилами, приклад, наведений нижче, для 太極 者 無極 而 生 з "a [LineBreak] = [LineBreak] 3" все ще працює
— Нір О.

45

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

7.9.1 Правила автоматичного введення крапки з комою

Існує три основні правила вставки крапки з комою:

  1. Коли, коли програма аналізується зліва направо, зустрічається маркер (який називається маркіруючим правопорушенням ), який не дозволений жодним випуском граматики, то перед крапкою правопорушника автоматично вставляється крапка з комою, якщо одна чи більше з наведених нижче умови вірні:
    • Маркер, що порушує право, відокремлений від попереднього маркера принаймні одним LineTerminator.
    • Знак образи є }.
  2. Коли програма розбирається зліва направо, зустрічається кінець вхідного потоку лексем, і аналізатор не в змозі проаналізувати потік вхідних токенів як єдиний повний ECMAScript Program, то в кінці пункту автоматично вводиться крапка з комою вхідний потік.
  3. Коли, коли програма аналізується зліва направо, зустрічається маркер, дозволений деяким випуском граматики, але виробництво є обмеженим виробництвом, і маркер буде першим жетоном для терміналу або нетерміналу, що знаходиться відразу після анотації " [немає LineTerminatorтут] " в межах обмеженого виробництва (і тому такий маркер називається обмеженим маркером), і обмежений маркер відокремлений від попереднього маркера щонайменше одним LineTerminator , після чого крапка з комою автоматично вставляється перед обмеженим маркером.

Однак у попередніх правилах є додаткова переважаюча умова: крапка з комою ніколи не вставляється автоматично, якщо крапка з комою буде розібрана як порожній вислів, або якщо ця крапка з комою стане однією з двох крапок з комою у заголовку forвисловлення (див. 12.6 .3).


44

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

Цитата:

JavaScript не розглядає кожен розрив рядка як крапку з комою: зазвичай розглядає розриви рядків як крапки з комою, лише якщо він не може проаналізувати код без крапки з комою.

Ще одна цитата: для коду

var a
a
=
3 console.log(a)

JavaScript не розглядає розрив другого рядка як крапку з комою, оскільки він може продовжувати розбір більш тривалого оператора a = 3;

і:

два винятки із загального правила, що JavaScript інтерпретує розриви рядків як крапки з комою, коли він не може проаналізувати другий рядок як продовження заяви на першому рядку. Перший виняток передбачає заяви про повернення, перерву та продовження

... Якщо після будь-якого з цих слів з’являється розрив рядка, JavaScript завжди буде інтерпретувати цей розрив як крапка з комою.

... Другий виняток включає операторів ++ та −− ... Якщо ви хочете використовувати будь-який з цих операторів як операторів постфіксу, вони повинні відображатися в тому ж рядку, що і вираз, до якого вони застосовуються. В іншому випадку розрив рядка буде розглядатися як крапка з комою, а ++ або - буде аналізуватися як оператор префікса, застосований до коду, що випливає. Розглянемо цей код, наприклад:

x 
++ 
y

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

Тому я думаю, щоб спростити це, це означає:

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

Частина про "ставитися до цього як до продовження коду до тих пір, поки це має сенс" дає відчуття, як жадібна відповідність регулярного виразу.

З урахуванням сказаного, що означає для returnрозриву рядка, інтерпретатор JavaScript вставить a;

(цитується ще раз: Якщо після будь-якого з цих слів з’являється розрив рядка [наприклад 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
— Zach Lysobey

1
JsHint / Lint прямо в редакторі коду з негайною відповіддю :)
— dmi3y

5
@balupton Коли кома, яка закінчила б рядок, буде забута, точка з комою автоматично вставляється. На відміну від правила, це більше нагадувало "ґутчу".
— Dexygen

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

3
Я думаю, що чіткість набору тексту var( let, const) у кожному рядку переважає частку секунди, необхідної для його введення.
— кальмар

5

Найбільш контекстуальний опис знайденого мною автоматичного вкладу JavaScript з крапкою з комою - це книга про майстерні перекладачі .

Правило JavaScript "автоматичне вставлення крапки з комою" є дивним. Якщо інші мови припускають, що більшість нових рядків є змістовними, і лише кілька слід ігнорувати в багаторядкових операторах, JS припускає зворотне. Він розглядає всі ваші нові рядки як безглузді пробіли, якщо у них не виникає помилка розбору. Якщо це так, він повертається назад і намагається перетворити попередній новий рядок у крапку з комою, щоб отримати щось граматично дійсне.

Він продовжує описувати це так, як ви б кодували запах .

Ця дизайнерська записка перетвориться на дизайнерський діатриб, якби я детально розказав про те, як це навіть працює, тим більше всі різні способи, що це погана ідея. Це безлад. JavaScript є єдиною мовою, яку я знаю, де багато посібників зі стилів вимагають явних крапок після кожного висловлювання, навіть якщо мова теоретично дозволяє вилучити їх.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.