Які правила для автоматичної вставки точки з комою в 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.