Quelles sont les règles pour l'insertion automatique de points-virgules (ASI) de JavaScript?


446

Eh bien, je devrais d'abord demander si cela dépend du navigateur.

J'ai lu que si un jeton non valide est trouvé, mais que la section de code est valide jusqu'à ce jeton non valide, un point-virgule est inséré avant le jeton s'il est précédé d'un saut de ligne.

Cependant, l'exemple commun cité pour les bogues causés par l'insertion de points-virgules est:

return
  _a+b;

..qui ne semble pas suivre cette règle, puisque _a serait un jeton valide.

D'un autre côté, la rupture des chaînes d'appels fonctionne comme prévu:

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

Quelqu'un at-il une description plus approfondie des règles?


22
Il y a une spécification ...
Miles

33
@Miles Tout simplement pas à votre lien cassé ;-) ecma-international.org/publications/standards/Ecma-262.htm
Zach Lysobey

3
Voir p. 26 du PDF cité ci-dessus.
2015


reportez-vous à la section 11.9 Insertion automatique de points-virgules
Andrew Lam

Réponses:


455

Tout d'abord, vous devez savoir quelles instructions sont affectées par l'insertion automatique de points-virgules (également connue sous le nom d'ASI par souci de concision):

  • déclaration vide
  • var déclaration
  • instruction d'expression
  • do-while déclaration
  • continue déclaration
  • break déclaration
  • return déclaration
  • throw déclaration

Les règles concrètes de l'ASI sont décrites dans la spécification §11.9.1 Règles d'insertion automatique des points-virgules

Trois cas sont décrits:

  1. Lorsqu'un jeton ( LineTerminatorou }) est rencontré qui n'est pas autorisé par la grammaire, un point-virgule est inséré devant lui si:

    • Le jeton est séparé du jeton précédent par au moins un LineTerminator.
    • Le jeton est }

    par exemple :

    { 1
    2 } 3

    est transformé en

    { 1
    ;2 ;} 3;

    Le NumericLiteral 1répond à la première condition, le jeton suivant est un terminateur de ligne.
    Le 2remplit la deuxième condition, le jeton suivant est }.

  2. Lorsque la fin du flux de jetons d'entrée est rencontrée et que l'analyseur n'est pas en mesure d'analyser le flux de jetons d'entrée en tant que programme complet unique, un point-virgule est automatiquement inséré à la fin du flux d'entrée.

    par exemple :

    a = b
    ++c

    se transforme en:

    a = b;
    ++c;
  3. Ce cas se produit lorsqu'un jeton est autorisé par une production de la grammaire, mais que la production est une production restreinte , un point-virgule est automatiquement inséré avant le jeton restreint.

    Productions restreintes:

    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

    L'exemple classique, avec ReturnStatement:

    return 
      "something";

    est transformé en

    return;
      "something";

4
# 1: Le jeton qui n'est pas autorisé par la grammaire n'est généralement pas un terminateur de ligne, n'est-ce pas (à moins que vous ne parliez des productions restreintes de # 3)? Il pense que vous devriez omettre les parenthèses. # 2 L'exemple ne devrait-il pas montrer seulement l'insertion après ++cpour plus de clarté?
Bergi

3
veuillez noter qu'ASI n'a pas besoin de réellement "insérer des points-virgules", juste pour terminer la déclaration dans l'analyseur d'un moteur ...
Aprillion

1
ce qu'il dit "flux d'entrée", cela signifie-t-il "une ligne"? Le "flux de jetons d'entrée" le rend un peu plus difficile à comprendre
non

Le lien de spécification fonctionne-t-il pour quelqu'un d'autre? Cela m'a conduit à une page presque vide qui avait un lien mort dessus.
intcreator

veuillez expliquer comment, selon ces règles, l'exemple ci-dessous par 太極 者 無極 而 生 de "a [LineBreak] = [LineBreak] 3" fonctionne toujours
Nir O.

45

Directement à partir de la spécification ECMAScript ECMA-262, cinquième édition :

7.9.1 Règles d'insertion automatique des points-virgules

Il existe trois règles de base pour l'insertion de points-virgules:

  1. Lorsque, lorsque le programme est analysé de gauche à droite, un jeton (appelé jeton incriminé ) est rencontré qui n'est autorisé par aucune production de la grammaire, un point-virgule est automatiquement inséré avant le jeton incriminé si un ou plusieurs des éléments suivants les conditions sont vraies:
    • Le jeton incriminé est séparé du jeton précédent par au moins un LineTerminator.
    • Le jeton fautif est }.
  2. Lorsque, lorsque le programme est analysé de gauche à droite, la fin du flux d'entrée de jetons est rencontrée et que l'analyseur n'est pas en mesure d'analyser le flux de jeton d'entrée en tant qu'un seul ECMAScript complet Program, un point-virgule est automatiquement inséré à la fin de la flux d'entrée.
  3. Lorsque, comme le programme est analysé de gauche à droite, un jeton est rencontré qui est autorisé par une certaine production de la grammaire, mais la production est une production restreinte et le jeton serait le premier jeton pour un terminal ou un non-terminal immédiatement après l'annotation " [non LineTerminatorici] " dans la production restreinte (et donc un tel jeton est appelé jeton restreint), et le jeton restreint est séparé du jeton précédent par au moins un LineTerminator , puis un point-virgule est automatiquement inséré avant le jeton restreint.

Cependant, il existe une condition de substitution supplémentaire sur les règles précédentes: un point-virgule n'est jamais inséré automatiquement si le point-virgule est ensuite analysé comme une instruction vide ou si ce point-virgule devient l'un des deux points-virgules dans l'en-tête d'une forinstruction (voir 12.6 .3).


44

Je ne pouvais pas trop bien comprendre ces 3 règles dans les spécifications - j'espère avoir quelque chose qui est plus simple en anglais - mais voici ce que j'ai recueilli de JavaScript: The Definitive Guide, 6th Edition, David Flanagan, O'Reilly, 2011:

Citation:

JavaScript ne traite pas chaque saut de ligne comme un point-virgule: il traite généralement les sauts de ligne comme des points-virgules uniquement s'il ne peut pas analyser le code sans les points-virgules.

Une autre citation: pour le code

var a
a
=
3 console.log(a)

JavaScript ne traite pas le deuxième saut de ligne comme un point-virgule car il peut continuer d'analyser l'instruction plus longue a = 3;

et:

deux exceptions à la règle générale selon laquelle JavaScript interprète les sauts de ligne comme des points-virgules lorsqu'il ne peut pas analyser la deuxième ligne en tant que continuation de l'instruction sur la première ligne. La première exception concerne les instructions return, break et continue

... Si un saut de ligne apparaît après l'un de ces mots ... JavaScript interprétera toujours ce saut de ligne comme un point-virgule.

... La deuxième exception concerne les opérateurs ++ et −− ... Si vous souhaitez utiliser l'un de ces opérateurs comme opérateurs postfix, ils doivent apparaître sur la même ligne que l'expression à laquelle ils s'appliquent. Sinon, le saut de ligne sera traité comme un point-virgule, et le ++ ou - sera analysé comme un opérateur de préfixe appliqué au code qui suit. Considérez ce code, par exemple:

x 
++ 
y

Il est analysé comme x; ++y;, pas commex++; y

Je pense donc que pour simplifier, cela signifie:

En général, JavaScript traitera comme continuation du code aussi longtemps qu'il est logique - sauf 2 cas: (1) après quelques mots clés comme return, break, continueet (2) si elle voit ++ou --sur une nouvelle ligne, il ajoutera le ;à la fin de la ligne précédente.

La partie sur "le traiter comme une continuation de code aussi longtemps que cela a du sens" donne l'impression d'une correspondance gourmande d'expression régulière.

Cela dit, cela signifie returnqu'avec un saut de ligne, l'interpréteur JavaScript insère un;

(cité à nouveau: si un saut de ligne apparaît après l'un de ces mots [tels que return] ... JavaScript interprétera toujours ce saut de ligne comme un point-virgule)

et pour cette raison, l'exemple classique de

return
{ 
  foo: 1
}

ne fonctionnera pas comme prévu, car l'interpréteur JavaScript le traitera comme:

return;   // returning nothing
{
  foo: 1
}

Il ne doit pas y avoir de saut de ligne immédiatement après return:

return { 
  foo: 1
}

pour qu'il fonctionne correctement. Et vous pouvez insérer un ;vous-même si vous deviez suivre la règle d'utilisation d'un ;après n'importe quelle déclaration:

return { 
  foo: 1
};

17

En ce qui concerne l'insertion de points-virgules et l'instruction var, méfiez-vous de la virgule lorsque vous utilisez var mais sur plusieurs lignes. Quelqu'un a trouvé cela dans mon code hier:

    var srcRecords = src.records
        srcIds = [];

Il a fonctionné, mais l'effet a été que la déclaration / affectation srcIds était globale car la déclaration locale avec var sur la ligne précédente ne s'appliquait plus car cette instruction était considérée comme terminée en raison de l'insertion automatique de points-virgules.


4
c'est pour ça que j'utilise jsLint
Zach Lysobey

1
JsHint / Lint directement dans votre éditeur de code avec une réponse immédiate :)
dmi3y

5
@balupton Lorsque la virgule qui aurait mis fin à la ligne est oubliée, un point-virgule est automatiquement inséré. Contrairement à une règle, cela ressemblait plus à un "gotcha".
Dexygen

1
Je pense que balupton est correct, c'est une différence si vous écrivez: var srcRecords = src.records srcIds = [];sur une ligne et oubliez la virgule ou que vous écrivez "return a && b" et n'oubliez rien ... mais la coupure de ligne avant l'a insèrerait un point-virgule automatique après le retour, qui est défini par les règles ASI ...
Sebastian

3
Je pense que la clarté de la frappe var( let, const) sur chaque ligne l'emporte sur la fraction de seconde qu'il faut pour la taper.
squidbe

5

La description la plus contextuelle de l' insertion automatique de points-virgules de JavaScript que j'ai trouvée provient d'un livre sur Crafting Interpreters .

La règle «d'insertion automatique de points-virgules» de JavaScript est la règle étrange. Lorsque d'autres langues supposent que la plupart des nouvelles lignes sont significatives et que seules quelques-unes doivent être ignorées dans les instructions multilignes, JS suppose le contraire. Il traite toutes vos nouvelles lignes comme des espaces vides de sens, sauf s'il rencontre une erreur d'analyse. Si c'est le cas, il revient en arrière et essaie de transformer la nouvelle ligne précédente en point-virgule pour obtenir quelque chose de grammaire valide.

Il continue à le décrire comme vous coderiez l'odeur .

Cette note de conception se transformerait en une diatribe de conception si j'entre dans les moindres détails sur la façon dont cela fonctionne même, et encore moins sur toutes les différentes façons dont c'est une mauvaise idée. C'est le bordel. JavaScript est le seul langage que je connaisse où de nombreux guides de style exigent des points-virgules explicites après chaque instruction, même si le langage vous permet théoriquement de les éluder.


En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.