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

533

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 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, dividir las cadenas de llamadas funciona como se esperaba:

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

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

6
534

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

  • 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 ofensivo que no está permitido por la gramática, se inserta un punto y coma delante de él si:
  • El token está separado del token anterior por al menos uno LineTerminator.
  • La ficha es }

por ejemplo :

    { 1
    2 } 3

se transforma en

    { 1
    ;2 ;} 3;

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

  1. Cuando se encuentra el final del flujo de entrada de tokens y el analizador no puede analizar el flujo de token 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;
  1. Este caso ocurre cuando un token está permitido por alguna producción de la gramática, pero la producción es una producción restringida , se inserta automáticamente un punto y coma 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 el ReturnStatement:

    return 
      "something";

se transforma en

    return;
      "something";
5
  • 4
    # 1: El token que no está permitido por la gramática generalmente no es un terminador de línea, ¿verdad (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 después ++cpara mayor claridad?
    Bergi
    9 de agosto de 2014 a las 12:50
  • 4
    tenga en cuenta que ASI no necesita realmente "insertar punto y coma", solo para terminar la declaración en el analizador de un motor ... 15 de enero de 2016 a las 22:16
  • 1
    lo que dice "flujo de entrada", ¿eso significa "una línea"? El "flujo de tokens de entrada" hace que sea un poco más difícil de entender 27/01/16 a las 14:58
  • ¿El enlace de especificaciones funciona para alguien más? Me llevó a una página casi vacía que tenía un enlace muerto. 15 de julio de 2016 a las 0:46
  • explique cómo, de acuerdo con estas reglas, el siguiente ejemplo de 太極 者 無極 而 生 de "a [LineBreak] = [LineBreak] 3" todavía funciona
    Nir O.
    21 de septiembre de 2017 a las 22:29
56

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

Cita:

JavaScript does not treat every line break as a semicolon: it usually treats line breaks as semicolons only if it can’t parse the code without the semicolons.

Otra cita: para el código

var a
a
=
3 console.log(a)

JavaScript does not treat the second line break as a semicolon because it can continue parsing the longer statement a = 3;

y:

two exceptions to the general rule that JavaScript interprets line breaks as semicolons when it cannot parse the second line as a continuation of the statement on the first line. The first exception involves the return, break, and continue statements

... If a line break appears after any of these words ... JavaScript will always interpret that line break as a semicolon.

... The second exception involves the ++ and −− operators ... If you want to use either of these operators as postfix operators, they must appear on the same line as the expression they apply to. Otherwise, the line break will be treated as a semicolon, and the ++ or -- will be parsed as a prefix operator applied to the code that follows. Consider this code, for example:

x 
++ 
y

It is parsed as x; ++y;, not as x++; y

Entonces creo que para 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" hace que parezca una coincidencia codiciosa de expresiones regulares.

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

(citado de nuevo: 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 salto de línea inmediatamente después de return:

return { 
  foo: 1
}

para que funcione correctamente. Y puede insertar un ;usted mismo si tuviera que seguir la regla de usar un ;después de cualquier declaración:

return { 
  foo: 1
};
49

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

7.9.1 Rules of Automatic Semicolon Insertion

There are three basic rules of semicolon insertion:

  1. When, as the program is parsed from left to right, a token (called the offending token) is encountered that is not allowed by any production of the grammar, then a semicolon is automatically inserted before the offending token if one or more of the following conditions is true:
    • The offending token is separated from the previous token by at least one LineTerminator.
    • The offending token is }.
  2. When, as the program is parsed from left to right, the end of the input stream of tokens is encountered and the parser is unable to parse the input token stream as a single complete ECMAScript Program, then a semicolon is automatically inserted at the end of the input stream.
  3. When, as the program is parsed from left to right, a token is encountered that is allowed by some production of the grammar, but the production is a restricted production and the token would be the first token for a terminal or nonterminal immediately following the annotation "[no LineTerminator here]" within the restricted production (and therefore such a token is called a restricted token), and the restricted token is separated from the previous token by at least one LineTerminator, then a semicolon is automatically inserted before the restricted token.

However, there is an additional overriding condition on the preceding rules: a semicolon is never inserted automatically if the semicolon would then be parsed as an empty statement or if that semicolon would become one of the two semicolons in the header of a for statement (see 12.6.3).

18

Con respecto a la inserción de punto y coma y la instrucción var, tenga cuidado de olvidar la coma cuando use var pero abarque 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 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.

6
  • 4
    este tipo de cosas es la razón por la que uso jsLint 18/04/2013 a las 17:46
  • 1
    JsHint / Lint directamente en su editor de código con respuesta inmediata :)
    dmi3y
    3 oct 2013 a las 19:33
  • 5
    @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 "te pillé".
    Dexygen
    25/07/2014 a las 13:19
  • 1
    Creo que balupton es correcto, es una diferencia si escribe: var srcRecords = src.records srcIds = [];en una línea y olvida la coma o escribe "return a && b" y no olvida nada ... pero el salto de línea antes de la a insertaría un punto y coma automático después de return, que está definido por las reglas de ASI ... 21/10/2014 a las 7:06
  • 4
    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
    1 de julio de 2018 a las 5:22
8

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 la elaboración de intérpretes .

JavaScript’s “automatic semicolon insertion” rule is the odd one. Where other languages assume most newlines are meaningful and only a few should be ignored in multi-line statements, JS assumes the opposite. It treats all of your newlines as meaningless whitespace unless it encounters a parse error. If it does, it goes back and tries turning the previous newline into a semicolon to get something grammatically valid.

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

This design note would turn into a design diatribe if I went into complete detail about how that even works, much less all the various ways that that is a bad idea. It’s a mess. JavaScript is the only language I know where many style guides demand explicit semicolons after every statement even though the language theoretically lets you elide them.

3

Solo para agregar,

const foo = function(){ return "foo" } //this doesn't add a semicolon here.
(function (){
    console.log("aa");
})()

vea esto, usando la expresión de función inmediatamente invocada (IIFE)