147 votes

JavaScript dispose-t-il d'une évaluation "court-circuit" ?

J'aimerais savoir si JavaScript dispose d'une évaluation "en court-circuit" comme l'opérateur && en C#. Si ce n'est pas le cas, j'aimerais savoir s'il existe une solution de contournement qu'il serait judicieux d'adopter.

161voto

gdoron Points 61066

Oui, JavaScript a une évaluation de "court-circuit".

if (true == true || foo.foo){
    // Passes, no errors because foo isn't defined.
}

Démonstration en direct

if (false && foo.foo){
    // Passes, no errors because foo isn't defined.
}

Démonstration en direct

60voto

Rawrplus Points 2878

Cette réponse explique en détail comment court-circuitage fonctionne en JavaScript, avec tous les pièges et les thèmes pertinents tels que la précédence des opérateurs, si vous cherchez une définition rapide et que vous comprenez déjà comment fonctionne le court-circuit, je vous recommande de consulter d'autres réponses.


Ce que nous (pensions) savoir jusqu'à présent :

Tout d'abord, inspectons le comportement que nous connaissons tous, à l'intérieur de l'objet if() où nous utilisons && pour vérifier si les deux choses sont true :

if (true && true) {
   console.log('bar');
} 

Maintenant, votre premier instinct est probablement de dire : Ah oui, c'est très simple, le code exécute la déclaration si les deux expr1 y expr2 sont évalués comme suit true '

Eh bien, oui et non. Vous avez techniquement raison, c'est le comportement que vous avez décrit, mais ce n'est pas exactement comme ça que le code est évalué et nous devrons creuser davantage pour bien comprendre.


Comment exactement le && y || interprétées ?

Il est temps de regarder " sous le capot " de la javascript moteur". Prenons cet exemple pratique :

function sanitise(x) {
  if (isNaN(x)) {
    return NaN;
  }
  return x;
}

let userinput = 0xFF; // as an example
const res = sanitise(userinput) && userinput + 5

console.log(res);

Eh bien, le résultat est 260 mais pourquoi ? Pour obtenir la réponse, nous devons comprendre comment fonctionne l'évaluation des courts-circuits.

Par le MDN Définition le site && opérateur en expr1 && expr2 est exécuté ensuite :

Si expr1 peut être converti en true , les retours expr2 ; sinon, renvoie expr1 .

Cela signifie donc que, dans notre exemple pratique, la const res est évalué de la manière suivante :

  1. Invoquer expr1 - sanitise(0xFF)
  2. 0xFF est un nombre hexadécimal valide pour 250, sinon je retournerais NaN
  3. El expr1 a retourné une valeur "véridique", le temps d'exécuter expr2 (sinon j'arrêterais comme NaN est faussé)
  4. Depuis userinput est véridique (un nombre), je peux ajouter +5 à elle
  • "Truthy" signifie que l'expression peut être évaluée comme vraie. Voici une liste de véridique y falsy expressions.

Donc, ici, nous avons été en mesure d'éviter des if et d'autres isNaN avec une simple utilisation de la fonction && opérateur.


Comment ça marche vraiment :

A ce stade, nous devrions au moins avoir une idée de la manière dont les court-circuit les opérateurs travaillent. La règle universelle est la suivante :

  • (some falsy expression) && expr sera évalué comme une expression faussée
  • (some truthy expression) || expr sera évaluée comme une expression véridique

Voici d'autres exemples pour une meilleure compréhension :

function a() { console.log('a'); return false; }
function b() { console.log('b'); return true; }

if ( a() && b() ){
     console.log('foobar'); 
}

//Evaluates a() as false, stops execution.

function a() { console.log('a'); return false; }
function b() { console.log('b'); return true; }

if ( a() || b() ){
     console.log('foobar'); 
}

/* 1. Evaluates a() as false
   2. So it should execute expr2, which is `b()`
   3. b() returned as true, executing statement `console.log('foobar');`
*/

Une dernière chose embêtante, mais très importante [la priorité de l'opérateur] :

Joli, j'espère que vous avez pris le coup de main ! La dernière chose que nous devons savoir est une règle sur la précédence des opérateurs, c'est-à-dire :

  • El && est toujours exécuté avant l'opérateur || opérateur.

Prenons l'exemple suivant :

function a() { console.log('a'); return true;}
function b() { console.log('b'); return false;}
function c() { console.log('c'); return false;}

console.log(a() || b() && c());

// returns a() and stops execution

Cela reviendra sous la forme, peut-être déroutante pour certains, de a() . La raison est assez simple, c'est juste notre vue qui nous trompe, car nous sommes habitués à lire de gauche à droite. Prenons le console.log() et ce qui ne l'est pas, et se concentrer uniquement sur l'évaluation.

true || false && false

Maintenant, il faut que tu te fasses à cette idée :

  1. Nous avons dit que le && est prioritaire, il est donc évalué en premier. Pour nous aider à mieux imaginer l'évaluation, pensez à la définition suivante

    expr1 && expr2

    Où :

    • expr2 es false
    • expr1 es true || false
  2. Donc c'était la partie délicate, maintenant true || false est évalué (le expr1 - côté gauche de la && ).

    • Compte tenu de la || L'opérateur arrête l'exécution si expr1 || expr2 en expr1 est évalué comme vrai, le expr1 est exécuté et l'exécution du code s'arrête.
  3. La valeur renvoyée est true

Eh bien c'était assez délicat, tout ça à cause de quelques règles et sémantiques bizarres. Mais rappelez-vous, vous pouvez toujours échapper à la préséance des opérateurs avec la fonction () - comme en mathématiques

function a() { console.log('a'); return true;}
function b() { console.log('b'); return false;}
function c() { console.log('c'); return false;}

console.log((a() || b()) && c());

/* 1. The () escape && operator precedence
   2. a() is evaluated as false, so expr2 (c()) to be executed
   3. c()  
*/

1voto

Roman Karagodin Points 112

L'idée est que les expressions logiques sont lues de gauche à droite, et si la valeur de la condition de gauche est suffisante pour obtenir la valeur totale, la condition de droite ne sera pas traitée et évaluée. Quelques exemples très simples :

function test() {
    const caseNumber = document.querySelector('#sel').value;
    const userChoice = () => confirm('Press OK or Cancel');
    if (caseNumber === '1') {
        console.log (1 === 1 || userChoice());
    } else if (caseNumber === '2') {
        console.log (1 === 2 && userChoice());
    } else if (caseNumber === '3') {
        console.log (1 === 2 || userChoice());
    } else if (caseNumber === '4') {
        console.log (1 === 1 && userChoice());
    } else if (caseNumber === '5') {
        console.log (userChoice() || 1 === 1);
    } else if (caseNumber === '6') {
        console.log (userChoice() && 1 === 2);
    }
}

<label for="sel">Select a number of a test case and press "RUN!":</label>
<br><select id="sel">
  <option value="">Unselected</option>
  <option value="1">Case 1</option>
  <option value="2">Case 2</option>
  <option value="3">Case 3</option>
  <option value="4">Case 4</option>
  <option value="5">Case 5</option>
  <option value="6">Case 6</option>
</select>
<button onclick="test()">RUN!</button>

Les deux premiers cas ci-dessus imprimeront les résultats dans la console true y false respectivement et vous ne verrez même pas la fenêtre modale vous demandant d'appuyer sur "OK" ou "Annuler", car la condition de gauche est suffisante pour définir le résultat total. Au contraire, avec les cas 3-6, vous verrez la fenêtre modale vous demandant votre choix, parce que les deux premiers dépendent de la partie droite (c'est-à-dire votre choix), et les deux derniers - indépendamment du fait que les valeurs agrégées de ces expressions ne dépendent pas de votre choix - parce que les conditions de gauche sont lues en premier. Il est donc important de placer les conditions de gauche à droite en fonction de celles que vous souhaitez voir traitées en premier.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X