web-dev-qa-db-fra.com

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

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 que le 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 courant cité pour les bogues causés par l'insertion d'un point-virgule est:

return
  _a+b;

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

Par contre, rompre les chaînes d’appel fonctionne comme prévu:

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

Quelqu'un a-t-il une description plus détaillée des règles?

376
T.R.

Tout d’abord, vous devez savoir quelles instructions sont affectées par l’insertion automatique du point-virgule (également appelé ASI pour abréger):

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

Les règles concrètes d’ASI sont décrites dans la spécification §11.9.1 Règles d’insertion automatique de point-virgule

Trois cas sont décrits:

  1. Lorsqu'un jeton (LineTerminator ou }) est rencontré et que la grammaire ne le permet pas, un point-virgule est inséré avant celui-ci 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;
    

    La NumericLiteral1 remplit la première condition, le jeton suivant est un terminateur de ligne.
    Le 2 remplit la deuxième condition, le jeton suivant est }.

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

    par exemple :

    a = b
    ++c
    

    est transformé en:

    a = b;
    ++c;
    
  3. Ce cas se produit lorsqu'un jeton est autorisé par une production de la grammaire alors qu'il s'agit d'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 le ReturnStatement:

    return 
      "something";
    

    est transformé en

    return;
      "something";
    
403
CMS

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

7.9.1 Règles d'insertion automatique du point-virgule

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

  1. Lorsque, comme le programme est analysé de gauche à droite, un jeton (appelé jeton offensant) est rencontré et qu'il n'est autorisé par aucune production de la grammaire, un point-virgule est automatiquement inséré avant le jeton incriminé. si une ou plusieurs des conditions suivantes sont remplies:
    • Le jeton en cause 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, le flux de jetons d'entrée se termine et que l'analyseur n'est pas en mesure d'analyser le flux de jetons d'entrée sous la forme d'un seul et complet ECMAScript Program, un point-virgule est automatiquement inséré à la fin de le flux d'entrée.
  3. Lorsque le programme est analysé de gauche à droite, on rencontre un jeton autorisé par certaines productions de la grammaire, mais la production est un production restreinte et le jeton serait le premier jeton pour une terminal ou non terminal suivant immédiatement l'annotation "[no LineTerminator here]"dans la production restreinte (et par conséquent un tel jeton est appelé jeton restreint), et le jeton restreint est séparé du jeton précédent par au moins un LineTerminator , un point-virgule est automatiquement inséré avant le jeton restreint.

Cependant, il existe une condition supplémentaire supplémentaire sur les règles précédentes: un point-virgule n’est jamais inséré automatiquement si le point-virgule était alors analysé comme une instruction vide ou si ce point-virgule devenait l’un des deux points-virgules de l’en-tête d’un objet. for déclaration (voir 12.6.3).

40
Jörg W Mittag

Je ne comprenais pas trop ces 3 règles dans les spécifications - j'espère quelque chose d'anglais plus clair - mais voici ce que j'ai recueilli auprès de JavaScript: Le Guide définitif, 6ème édition, 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 à 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 suite de l'instruction sur la première ligne. La première exception concerne les déclarations 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 en tant qu'opérateurs postfixes, 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érons ce code, par exemple:

x 
++ 
y

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

Donc, je pense que pour simplifier, cela signifie:

En général, JavaScript le traitera comme une continuation du code tant que cela aura du sens - sauf deux cas: (1) après quelques mots clés comme return, break, continue, et (2) s'il voit ++ ou -- sur une nouvelle ligne, il ajoutera le ; à la fin de la ligne précédente.

La partie "Traitez-le comme une continuation du code tant que cela a du sens" donne l'impression que la correspondance avide d'une expression régulière est utilisée.

Cela dit, cela signifie que pour return avec un saut de ligne, l'interpréteur JavaScript insérera un ;

(citée à nouveau: si un saut de ligne apparaît après l'un de ces mots [tel 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 suit:

return;   // returning nothing
{
  foo: 1
}

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

return { 
  foo: 1
}

pour que cela fonctionne correctement. Et vous pouvez insérer vous-même un ; si vous deviez suivre la règle d'utilisation d'un ; après une déclaration quelconque:

return { 
  foo: 1
};
34

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

    var srcRecords = src.records
        srcIds = [];

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

17
George Jempty

La description la plus contextuelle de JavaScript Insertion automatique de point-virgule 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 des impairs. Là où d'autres langues supposent que la plupart des nouvelles lignes ont un sens et que seules quelques-unes devraient ê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 tel est le cas, il retourne en arrière et tente de transformer la nouvelle ligne précédente en point-virgule pour obtenir quelque chose de grammaticalement valide.

Il continue en le décrivant comme vous le feriez odeur de code .

Cette note de conception se transformerait en une diatribe de conception si je décrivais en détail comment cela fonctionnait, et encore moins toutes les différentes façons dont c’était une mauvaise idée. C'est le bordel. JavaScript est le seul langage que je connaisse où de nombreux guides de styles exigent des points-virgules explicites après chaque énoncé, même si le langage vous permet théoriquement de les écarter.

1
jchook