J'ai lu this question à propos de "l'opérateur de virgule" dans les expressions (,
) et de la documentation MDN à ce sujet, mais je ne vois pas de scénario où cela serait utile.
Alors, quand l'opérateur de virgule est-il utile?
Ce qui suit n’est probablement pas très utile car vous ne l’écrivez pas vous-même, mais un minifier peut réduire le code à l’aide de l’opérateur virgule. Par exemple:
if(x){foo();return bar()}else{return 1}
deviendrait:
return x?(foo(),bar()):1
L'opérateur ? :
peut maintenant être utilisé, car l'opérateur de virgule (dans une certaine mesure) permet d'écrire deux instructions en une seule.
Cette est utile dans la mesure où elle permet une compression nette (39 -> 24 octets ici).
J'aimerais souligner le fait que la virgule dans var a, b
est non l'opérateur de virgule, car il n'existe pas dans une expression. La virgule a une signification particulière dans var
statement. a, b
dans une expression ferait référence aux deux variables et serait évalué à b
, ce qui n'est pas le cas pour var a, b
.
L'opérateur de virgule vous permet de placer plusieurs expressions à un endroit où une expression est attendue. La valeur résultante de plusieurs expressions séparées par une virgule sera la valeur de la dernière expression séparée par une virgule.
Personnellement, je ne l'utilise pas très souvent car il n'y a pas beaucoup de situations dans lesquelles plus d'une expression est attendue et il n'y a pas de moyen moins déroutant d'écrire le code que d'utiliser l'opérateur de virgule. Une possibilité intéressante est à la fin d'une boucle for
lorsque vous souhaitez incrémenter plus d'une variable:
// j is initialized to some other value
// as the for loop executes both i and j are incremented
// because the comma operator allows two statements to be put in place of one
for (var i = 0; i < items.len; i++, j++) {
// loop code here that operates on items[i]
// and sometimes uses j to access a different array
}
Ici, vous voyez que i++, j++
peut être placé à un endroit où une expression est autorisée. Dans ce cas particulier, les expressions multiples sont utilisées pour les effets latéraux; il n’est donc pas important que les expressions composées prennent la valeur du dernier, mais il existe d’autres cas dans lesquels cela pourrait avoir de l’importance.
L'opérateur de virgule est souvent utile lors de l'écriture de code fonctionnel en Javascript.
Considérez ce code que j'ai écrit pour un SPA il y a quelque temps et qui ressemblait à ce qui suit
const actions = _.chain(options)
.pairs() // 1
.filter(selectActions) // 2
.map(createActionPromise) // 3
.reduce((state, pair) => (state[pair[0]] = pair[1], state), {}) // 4
.value();
C'était un scénario assez complexe, mais réel. Restez avec moi pendant que j'explique ce qui se passe et, dans le même temps, plaide en faveur de l'opérateur de virgule .
Décomposez toutes les options passées à cette fonction en utilisant pairs
Ce qui transformera { a: 1, b: 2}
en [['a', 1], ['b', 2]]
Ce tableau de paires de propriétés est filtré en fonction de celles qui sont considérées comme des "actions" dans le système.
Ensuite, le deuxième index du tableau est remplacé par une fonction qui renvoie une promesse représentant cette action (à l'aide de map
)
Enfin, l'appel à reduce
fusionnera chaque "tableau de propriétés" (['a', 1]
) dans un objet final.
Le résultat final est une version transformée de l'argument options
, qui contient uniquement les clés appropriées et dont les valeurs sont consommables par la fonction appelante.
En regardant juste
.reduce((state, pair) => (state[pair[0]] = pair[1], state), {})
Vous pouvez voir que la fonction de réduction commence par un objet d'état vide, state
, et que pour chaque paire représentant une clé et une valeur, la fonction renvoie le même objet state
après avoir ajouté une propriété à l'objet correspondant à la paire clé/valeur. En raison de la syntaxe de la fonction de flèche de ECMAScript 2015, le corps de la fonction est une expression. Par conséquent, l'opérateur de virgule permet une fonction "iteratee" concise et utile.
Personnellement, j’ai rencontré de nombreux cas lors de l’écriture de Javascript dans un style plus fonctionnel avec ECMAScript 2015 + Arrow Functions. Cela dit, avant de rencontrer des fonctions fléchées (comme au moment de la rédaction de la question), je n'avais jamais utilisé l'opérateur de virgule de manière délibérée.
Une autre utilisation de l'opérateur de virgule consiste à masquer les résultats qui ne vous intéressent pas dans la réplique ou la console, uniquement pour des raisons de commodité.
Par exemple, si vous évaluez myVariable = aWholeLotOfText
dans la répl ou la console, toutes les données que vous venez d'attribuer seront imprimées. Cela peut être des pages et des pages, et si vous préférez ne pas le voir, vous pouvez plutôt évaluer myVariable = aWholeLotOfText, 'done'
, et repl/console imprimera simplement «done».
Oriel souligne correctement † this custom toString()
ou get()
les fonctions pourraient même rendre cela utile.
L'opérateur de virgule n'est pas spécifique à JavaScript, il est disponible dans d'autres langages comme C et C++ . En tant qu'opérateur binaire, cela est utile lorsque le premier opérande, qui est généralement une expression, a l'effet secondaire souhaité requis par le deuxième opérande. Un exemple de Wikipédia:
i = a += 2, a + b;
Évidemment, vous pouvez écrire deux lignes de codes différentes, mais utiliser une virgule est une autre option, parfois plus lisible.
Je ne suis pas d’accord avec Flanagan et je dirais que la virgule est vraiment utile et permet d’écrire du code plus lisible et plus élégant, surtout quand vous savez ce que vous faites:
Voici le article très détaillé sur l'utilisation de virgule:
Plusieurs exemples de là-bas pour la preuve de démonstration:
function renderCurve() {
for(var a = 1, b = 10; a*b; a++, b--) {
console.log(new Array(a*b).join('*'));
}
}
Un générateur de fibonacci:
for (
var i=2, r=[0,1];
i<15;
r.Push(r[i-1] + r[i-2]), i++
);
// 0,1,1,2,3,5,8,13,21,34,55,89,144,233,377
Trouver le premier élément parent, analogue à la fonction jQuery .parent()
:
function firstAncestor(el, tagName) {
while(el = el.parentNode, el && (el.tagName != tagName.toUpperCase()));
return el;
}
//element in http://ecma262-5.com/ELS5_HTML.htm
var a = $('Section_15.1.1.2');
firstAncestor(a, 'div'); //<div class="page">
Je n'ai pas trouvé d'utilisation pratique autre que celle-ci, mais voici un scénario dans lequel James Padolsey utilise joliment cette technique pour la détection IE dans une boucle while:
var ie = (function(){
var undef,
v = 3,
div = document.createElement('div'),
all = div.getElementsByTagName('i');
while ( // <-- notice no while body here
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
);
return v > 4 ? v : undef;
}());
Ces deux lignes doivent exécuter:
div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]
Et à l’intérieur de l’opérateur de virgule, les deux sont évalués bien que l’on ait pu les transformer en déclarations séparées.
Il y a quelque chose d '"étrange" qui peut être fait dans JavaScript en appelant une fonction indirectement en utilisant l'opérateur virgule.
Il existe une description longue ici: Appel de fonction indirect en JavaScript
En utilisant cette syntaxe:
(function() {
"use strict";
var global = (function () { return this || (1,eval)("this"); })();
console.log('Global === window should be true: ', global === window);
var not_global = (function () { return this })();
console.log('not_global === window should be false: ', not_global === window);
}());
Vous pouvez avoir accès à la variable globale car eval
fonctionne différemment lorsqu’il est appelé directement ou indirectement.
Je finis par l’utiliser lors de l’analyse facultative des arguments. Je pense que cela le rend plus lisible et plus concis, de sorte que l'analyse des arguments ne domine pas le corps de la fonction.
/**
* @param {string} [str]
* @param {object} [obj]
* @param {Date} [date]
*/
function f(str, obj, date) {
// handle optional arguments
if (typeof str !== "string") date = obj, obj = str, str = "default";
if (obj instanceof Date) date = obj, obj = {};
if (!(date instanceof Date)) date = new Date();
// ...
}
Disons que vous avez un tableau:
arr = [];
Lorsque vous Push
sur ce tableau, vous êtes rarement intéressé par la valeur de retour de Push
, à savoir la nouvelle longueur du tableau, mais plutôt par le tableau lui-même:
arr.Push('foo') // ['foo'] seems more interesting than 1
En utilisant l'opérateur virgule, nous pouvons pousser sur le tableau, spécifier le tableau comme dernier opérande à virgule, puis utiliser le résultat - le tableau lui-même - pour un appel de méthode tableau suivant, une sorte de chaînage:
(arr.Push('bar'), arr.Push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]
J'ai trouvé l'opérateur de virgule le plus utile lorsque vous écrivez des aides de ce type.
const stopPropagation = event => (event.stopPropagation(), event);
const preventDefault = event => (event.preventDefault(), event);
const both = compose(stopPropagation, preventDefault);
Vous pouvez remplacer la virgule par un || ou &&, mais vous devez alors savoir ce que la fonction renvoie.
Plus important encore, le séparateur de virgule communique intention - le code ne s’intéresse pas à ce que l’opérande de gauche évalue, alors que les alternatives peuvent avoir une autre raison d’être là. Cela facilite la compréhension et la refactorisation. Si le type de retour de fonction change, le code ci-dessus ne sera pas affecté.
Naturellement, vous pouvez obtenir la même chose par d'autres moyens, mais pas aussi succinctement. Si || et && ont trouvé une place dans l’utilisation courante, tout comme l’opérateur par virgule.