¿Cuáles son las reglas para la inserción automática de punto y coma (ASI) de JavaScript?

445

Bueno, primero probablemente debería preguntar si esto depende del navegador.

He leído que si se encuentra un token no válido, pero la sección de código es válida hasta ese token no válido, se inserta un punto y coma antes del token si está precedido por un salto de línea.

Sin embargo, el ejemplo común citado para los errores causados ​​por la inserción de punto y coma es:

return
  _a+b;

..que no parece seguir esta regla, ya que _a sería un token válido.

Por otro lado, romper las cadenas de llamadas funciona como se esperaba:

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

¿Alguien tiene una descripción más detallada de las reglas?

TR
fuente
22
No es una especificación ...
Miles
33
@Miles Simplemente no en su enlace roto ;-) ecma-international.org/publications/standards/Ecma-262.htm
Zach Lysobey
3
Ver p. 26 del PDF citado anteriormente.
ᴠɪɴᴄᴇɴᴛ
consulte la sección 11.9 Inserción automática de punto y coma
Andrew Lam

Respuestas:

454

En primer lugar, debe saber qué declaraciones se ven afectadas por la inserción automática de punto y coma (también conocido como ASI por brevedad):

  • declaración vacía
  • var declaración
  • declaración de expresión
  • do-while declaración
  • continue declaración
  • break declaración
  • return declaración
  • throw declaración

Las reglas concretas de ASI se describen en la especificación §11.9.1 Reglas de inserción automática de punto y coma

Se describen tres casos:

  1. Cuando se encuentra un token ( LineTerminatoro }) que no está permitido por la gramática, se inserta un punto y coma antes si:

    • El token está separado del token anterior por al menos uno LineTerminator.
    • El token es }

    por ejemplo :

    { 1
    2 } 3

    se transforma en

    { 1
    ;2 ;} 3;

    El NumericLiteral 1cumple con la primera condición, el siguiente token es un terminador de línea.
    El 2cumple con la segunda condición, el siguiente token es }.

  2. Cuando se encuentra el final del flujo de entrada de tokens y el analizador no puede analizar el flujo de tokens de entrada como un único Programa completo, se inserta automáticamente un punto y coma al final del flujo de entrada.

    por ejemplo :

    a = b
    ++c

    se transforma en:

    a = b;
    ++c;
  3. Este caso ocurre cuando un token es permitido por alguna producción de la gramática, pero la producción es una producción restringida , un punto y coma se inserta automáticamente antes del token restringido.

    Producciones restringidas:

    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

    El ejemplo clásico, con ReturnStatement:

    return 
      "something";

    se transforma en

    return;
      "something";
CMS
fuente
44
# 1: El token que no está permitido por la gramática generalmente no es un terminador de línea, ¿no es así (a menos que se refiera a las producciones restringidas del # 3)? Piensa que deberías omitir los paréntesis. # 2 ¿No debería el ejemplo mostrar solo la inserción posterior ++cpara mayor claridad?
Bergi
2
tenga en cuenta que ASI no necesita realmente "insertar punto y coma", solo para terminar la declaración en el analizador de un motor ...
Aprillion
1
lo que dice "flujo de entrada", ¿eso significa "una línea"? El "flujo de token de entrada" hace que sea un poco más difícil de entender
no
¿El enlace de especificaciones funciona para alguien más? Me llevó a una página casi vacía que tenía un enlace muerto.
intcreator
explique cómo, de acuerdo con estas reglas, el ejemplo a continuación por 太極 者 無極 而 生 de "a [LineBreak] = [LineBreak] 3" todavía funciona
Nir O.
45

Directamente de la ECMA-262, quinta edición de la especificación ECMAScript :

7.9.1 Reglas de inserción automática de punto y coma

Hay tres reglas básicas de inserción de punto y coma:

  1. Cuando, a medida que el programa se analiza de izquierda a derecha, se encuentra un token (llamado token infractor ) que no está permitido por ninguna producción de la gramática, entonces se inserta automáticamente un punto y coma antes del token infractor si uno o más de los siguientes las condiciones son ciertas:
    • El token infractor está separado del token anterior por al menos uno LineTerminator.
    • La ficha ofensiva es }.
  2. Cuando, cuando el programa se analiza de izquierda a derecha, se encuentra el final del flujo de entrada de tokens y el analizador no puede analizar el flujo de tokens de entrada como un único ECMAScript completo Program, se inserta automáticamente un punto y coma al final del flujo de entrada.
  3. Cuando, a medida que el programa se analiza de izquierda a derecha, se encuentra un token que está permitido por alguna producción de la gramática, pero la producción es una producción restringida y el token sería el primer token para un terminal o no terminal inmediatamente después de la anotación " [no LineTerminatoraquí] " dentro de la producción restringida (y, por lo tanto, tal token se denomina token restringido), y el token restringido se separa del token anterior por al menos un LineTerminator , luego se inserta automáticamente un punto y coma antes del token restringido.

Sin embargo, hay una condición de anulación adicional en las reglas anteriores: un punto y coma nunca se inserta automáticamente si el punto y coma se analiza como una declaración vacía o si ese punto y coma se convertiría en uno de los dos puntos y coma en el encabezado de una fordeclaración (ver 12.6 .3).

Jörg W Mittag
fuente
44

No podía entender muy bien esas 3 reglas en las especificaciones, espero tener algo que sea más simple en inglés, pero esto es lo que obtuve de JavaScript: The Definitive Guide, 6th Edition, David Flanagan, O'Reilly, 2011:

Citar:

JavaScript no trata cada salto de línea como un punto y coma: por lo general, trata los saltos de línea como punto y coma solo si no puede analizar el código sin los puntos y coma.

Otra cita: para el código

var a
a
=
3 console.log(a)

JavaScript no trata el segundo salto de línea como un punto y coma porque puede continuar analizando la declaración más larga a = 3;

y:

dos excepciones a la regla general de que JavaScript interpreta los saltos de línea como punto y coma cuando no puede analizar la segunda línea como una continuación de la declaración en la primera línea. La primera excepción implica las declaraciones de devolución, interrupción y continuación.

... Si aparece un salto de línea después de cualquiera de estas palabras ... JavaScript siempre interpretará ese salto de línea como un punto y coma.

... La segunda excepción involucra a los operadores ++ y −− ... Si desea utilizar cualquiera de estos operadores como operadores de postfix, deben aparecer en la misma línea que la expresión a la que se aplican. De lo contrario, el salto de línea se tratará como un punto y coma, y ​​el ++ o - se analizará como un operador de prefijo aplicado al código que sigue. Considere este código, por ejemplo:

x 
++ 
y

Se analiza como x; ++y;, no comox++; y

Entonces creo que simplificarlo, eso significa:

En general, JavaScript lo tratará como continuación del código, siempre y cuando tenga sentido - excepto 2 casos: (1) Después de algunas palabras clave como return, break, continue, y (2) si se ve ++o --en una nueva línea, entonces se añadirá el ;al final de la línea anterior.

La parte sobre "tratarlo como una continuación del código siempre que tenga sentido" lo hace sentir como la codiciosa coincidencia de la expresión regular.

Dicho lo anterior, eso significa que returncon un salto de línea, el intérprete de JavaScript insertará un;

(citado nuevamente: si aparece un salto de línea después de cualquiera de estas palabras [como return] ... JavaScript siempre interpretará ese salto de línea como un punto y coma)

y por esta razón, el ejemplo clásico de

return
{ 
  foo: 1
}

no funcionará como se esperaba, porque el intérprete de JavaScript lo tratará como:

return;   // returning nothing
{
  foo: 1
}

No debe haber un salto de línea inmediatamente después de return:

return { 
  foo: 1
}

para que funcione correctamente. Y puede insertar un ;mensaje si sigue la regla de usar un ;después de cualquier declaración:

return { 
  foo: 1
};
nonopolaridad
fuente
17

Con respecto a la inserción de punto y coma y la declaración var, tenga cuidado de olvidar la coma cuando se usa var pero abarca varias líneas. Alguien encontró esto en mi código ayer:

    var srcRecords = src.records
        srcIds = [];

Se ejecutó, pero el efecto fue que la declaración / asignación de srcIds era global porque la declaración local con var en la línea anterior ya no se aplicaba ya que esa declaración se consideraba terminada debido a la inserción automática de punto y coma.

Dexygen
fuente
44
esto es por eso que uso jsLint
Zach Lysobey
1
JsHint / Lint directamente en su editor de código con respuesta inmediata :)
dmi3y
55
@balupton Cuando se olvida la coma que habría terminado la línea, se inserta automáticamente un punto y coma. A diferencia de una regla, era más como un "gotcha".
Dexygen
1
Creo que Balupton es correcto, es una diferencia si escribes: var srcRecords = src.records srcIds = [];en una línea y olvidas la coma o escribes "return a & b" y no olvidas nada ... pero el salto de línea antes de la a insertaría un punto y coma automático después de la devolución, que se define por las reglas ASI ...
Sebastian
3
Creo que la claridad de escribir var( let, const) en cada línea supera la fracción de segundo que se necesita para escribirla.
squidbe 01 de
5

La descripción más contextual de la inserción automática de punto y coma de JavaScript que he encontrado proviene de un libro sobre Crafting Interpreters .

La regla de "inserción automática de punto y coma" de JavaScript es la extraña. Cuando otros lenguajes suponen que la mayoría de las nuevas líneas son significativas y solo unas pocas deben ignorarse en las declaraciones de varias líneas, JS supone lo contrario. Trata todas sus líneas nuevas como espacios en blanco sin sentido a menos que encuentre un error de análisis. Si lo hace, retrocede e intenta convertir la nueva línea anterior en punto y coma para obtener algo gramaticalmente válido.

Continúa describiéndolo como codificarías el olor .

Esta nota de diseño se convertiría en una diatriba de diseño si entrara en detalles completos sobre cómo funciona eso, y mucho menos sobre las diversas formas en que eso es una mala idea. Es un desastre. JavaScript es el único lenguaje que sé donde muchas guías de estilo exigen puntos y comas explícitos después de cada declaración, aunque el lenguaje teóricamente te permite eludirlos.

jchook
fuente