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.
Réponses
Trop de publicités?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 enexpr1 && expr2
est exécuté ensuite :Si
expr1
peut être converti entrue
, les retoursexpr2
; sinon, renvoieexpr1
.
Cela signifie donc que, dans notre exemple pratique, la const res
est évalué de la manière suivante :
- Invoquer
expr1
-sanitise(0xFF)
-
0xFF
est un nombre hexadécimal valide pour 250, sinon je retourneraisNaN
- El
expr1
a retourné une valeur "véridique", le temps d'exécuterexpr2
(sinon j'arrêterais commeNaN
est faussé) - Depuis
userinput
est véridique (un nombre), je peux ajouter+5
à elle
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 :
-
Nous avons dit que le
&&
est prioritaire, il est donc évalué en premier. Pour nous aider à mieux imaginer l'évaluation, pensez à la définition suivanteexpr1 && expr2
Où :
-
expr2
esfalse
-
expr1
estrue || false
-
-
Donc c'était la partie délicate, maintenant
true || false
est évalué (leexpr1
- côté gauche de la&&
).- Compte tenu de la
||
L'opérateur arrête l'exécution siexpr1 || expr2
enexpr1
est évalué comme vrai, leexpr1
est exécuté et l'exécution du code s'arrête.
- Compte tenu de la
- 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()
*/
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.