Quand l'opérateur virgule est-il utile?

88

J'ai lu cette question sur «l'opérateur virgule» dans expressions ( ,) et la documentation MDN à ce sujet, mais je ne peux pas penser à un scénario où il est utile.

Alors, quand l'opérateur virgule est-il utile?

gdoron soutient Monica
la source
2
var i, j, k;vs var i; var j, var k?
Salman A
15
@SalmanA. Je ne suis pas sûr que cela ait quoi que ce soit à voir avec l' ,opérateur. Cette ligne est également valide dans C#, mais l' ,opérateur n'existe pas là-bas.
gdoron soutient Monica
2
@SalmanA. J'ai fait. Je ne l'ai pas trouvé, enlight me ...
gdoron soutient Monica
5
@SalmanA a ,n'est pas toujours l' ,opérateur (et ce n'est jamais l' ,opérateur en C #). Ainsi, C # peut manquer d' ,opérateur tout en l'utilisant librement ,dans le cadre de la syntaxe.
Seth Carnegie
8
Je pense que les réponses ici ont résumé le fait que le ,n'est pas largement utilisé (et toutes les occurrences de a ne ,sont pas l'opérateur virgule) . Mais vous pouvez l'emprunter et un tableau pour effectuer un échange de variables en ligne sans créer de variable temporaire. Étant donné que vous souhaitez échanger les valeurs de aet b, vous pouvez le faire a = [b][b = a,0]. Cela place le courant bdans le tableau. La seconde []est la notation d'accès aux propriétés. L'indice est accessible 0, mais pas avant d' attribuer aà b, qui est maintenant en sécurité depuis best conservée dans le tableau. le ,permet de faire les multiples expressions dans le [].

Réponses:

128

Ce qui suit n'est probablement pas très utile car vous ne l'écrivez pas vous-même, mais un minificateur 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 être utilisé maintenant, car l'opérateur virgule (dans une certaine mesure) permet d'écrire deux instructions comme une seule instruction.

Ceci est utile dans la mesure où cela permet une compression nette (39 -> 24 octets ici).


Je tiens à souligner le fait que la virgule var a, bn'est pas l'opérateur virgule car elle n'existe pas dans une expression . La virgule a une signification particulière dans les var déclarations . a, bdans une expression ferait référence aux deux variables et évaluerait b, ce qui n'est pas le cas pour var a, b.

pimvdb
la source
3
Comment avez-vous pensé à cela? L'avez-vous lu quelque part? Est-ce vraiment utilisé?
gdoron soutient Monica
16
J'étais juste en train de jouer avec Closure Compiler l'autre jour pour voir ce qu'il fait réellement, et j'ai remarqué cette substitution.
pimvdb
2
Une utilisation similaire qui, je pense, est utile dans votre code serait pour attribuer plusieurs variables dans une instruction if en ligne. Par exemple: if (condition) var1 = val1, var2 = val2;je pense personnellement qu'éviter les crochets lorsque cela est possible rend le code plus lisible.
Aidiakapi
2
Les seules fois où j'utilise l'opérateur virgule, c'est lorsque j'ajoute des instructions de journal à des expressions (foo => (console.log ('foo', foo), foo)), ou si je deviens trop intelligent avec des itérations . (pairs.reduce ((acc, [k, v]) => (acc [k] = v, acc), {}))
Joseph Sikorski
38

L'opérateur 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 des virgules.

Personnellement, je ne l'utilise pas très souvent car il n'y a pas beaucoup de situations où plus d'une expression est attendue et il n'y a pas de façon moins déroutante d'écrire le code que d'utiliser l'opérateur virgule. Une possibilité intéressante est à la fin d'une forboucle lorsque vous voulez que plus d'une variable soit incrémentée:

// 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 qui i++, j++peut être placé dans un endroit où une expression est autorisée. Dans ce cas particulier, les expressions multiples sont utilisées pour les effets secondaires, donc peu importe que les expressions composées prennent la valeur de la dernière, mais il existe d'autres cas où cela peut réellement avoir de l'importance.

jfriend00
la source
38

L'opérateur 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 qui avait quelque chose comme 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, ce faisant, plaidez pour l'opérateur virgule.


Cela utilise le chaînage de Underscore pour

  1. Démonter toutes les options passées à cette fonction à l' aide pairs qui va se transformer { a: 1, b: 2}en[['a', 1], ['b', 2]]

  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.

  3. Ensuite, le deuxième index du tableau est remplacé par une fonction qui renvoie une promesse représentant cette action (en utilisant map)

  4. Enfin, l'appel à reducefusionnera chaque "tableau de propriétés" ( ['a', 1]) en un objet final.

Le résultat final est une version transformée de l' optionsargument, 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 pour chaque paire représentant une clé et une valeur, la fonction renvoie le même stateobjet 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 d'ECMAScript 2015, le corps de la fonction est une expression et, par conséquent, l'opérateur virgule permet une fonction "iteratee" concise et utile .

Personnellement, j'ai rencontré de nombreux cas en écrivant du 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 l'écriture de la question), je n'avais jamais utilisé l'opérateur virgule de manière délibérée.

Syynth
la source
2
C'est la seule réponse vraiment utile en ce qui concerne comment / quand un programmeur peut-il utiliser l'opérateur virgule. Très utile surtout dansreduce
hgoebl
1
Avec ES6 +, cela devrait être la réponse acceptée.
Ardee Aram le
Belle réponse, mais si je peux suggérer quelque chose un peu plus lisible: .reduce((state, [key, value]) => (state[key] = value, state), {}). Et je me rends compte que cela va à l'encontre du but de la réponse, mais .reduce((state, [key, value]) => Object.assign(state, { [key]: value }), {})éliminerait complètement le besoin de l'opérateur virgule.
Patrick Roberts
Bien que Object.assign soit probablement plus idiomatique ces jours-ci, ou même simplement l'opérateur de diffusion, je ne suis pas sûr qu'ils étaient largement utilisés à l'époque. Je voudrais également souligner que si l'opérateur virgule est un peu plus obscur, cela pourrait générer beaucoup moins de déchets dans les situations où l'ensemble de données est très grand. La déstructuration aiderait certainement la lisibilité!
Syynth
18

Une autre utilisation de l'opérateur virgule est de masquer les résultats qui ne vous intéressent pas dans la réplique ou la console, pour des raisons de commodité.

Par exemple, si vous évaluez myVariable = aWholeLotOfTextdans le repl ou la console, il imprimera toutes les données que vous venez d'attribuer. Cela peut être des pages et des pages, et si vous préférez ne pas le voir, vous pouvez à la place évaluer myVariable = aWholeLotOfText, 'done', et le repl / console affichera simplement «done».

Oriel souligne correctement que des fonctions toString()ou des get()fonctions personnalisées peuvent même rendre cela utile.

Julian de Bhal
la source
1
Ha, très belle idée! (Enfin une réponse qui répond réellement à la question contrairement à presque toutes les réponses {et 3 réponses supprimées dont vous avez besoin de 20K de réputation pour voir ...})
gdoron soutient Monica le
1
Si la valeur assignée est un objet, la console peut tenter de l'afficher correctement. Pour ce faire, il pourrait par exemple appeler des getters, ce qui pourrait changer l'état de l'objet. L'utilisation d'une virgule peut empêcher cela.
Oriol
@Oriol - Bien! Vous avez tout à fait raison. D'une manière ou d'une autre, cela étant potentiellement utile semble un peu décevant :)
Julian de Bhal
13

L'opérateur 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 un effet secondaire souhaité requis par le second opérande. Un exemple de wikipedia:

i = a += 2, a + b;

Évidemment, vous pouvez écrire deux lignes de codes différentes, mais l'utilisation de la virgule est une autre option et parfois plus lisible.

taskinoor
la source
1
Considérez cela comme une alternative, bien que la définition du bien puisse varier d'une personne à l'autre. Cependant, je ne trouve aucun exemple où vous DEVEZ utiliser une virgule. Une autre chose similaire est ternaire?: Opérateur. Cela peut toujours être remplacé par if-else mais parfois?: Rend le code plus lisible que if-else. Le même concept vaut également pour la virgule.
taskinoor
BTW, je ne considère pas l'utilisation de la virgule dans la déclaration de variable ou l'initialisation de plusieurs variables en boucle. Dans ces cas, la virgule est généralement meilleure.
taskinoor
2
cela semble déroutant af ... wtf.
Timmerz
6

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 élégant, surtout quand vous savez ce que vous faites:

Voici l' article très détaillé sur l'utilisation de la 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

Trouvez le premier élément parent, analogue de la .parent()fonction jQuery :

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">
shaman.sir
la source
7
Je ne sais pas si je dirais que tout cela est plus lisible, mais c'est certainement assez spiffy donc +1
Chris Marisic
1
Vous n'avez pas besoin de la virgule dans la boucle while dans le dernier exemple, ce while ((el = el.parentNode) && (el.tagName != tagName.toUpperCase()))serait très bien dans ce contexte.
PitaJ
5

Je n'ai pas trouvé d'utilisation pratique autre que cela, mais voici un scénario dans lequel James Padolsey utilise bien 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 s'exécuter:

div.innerHTML = '<!--[if gt IE ' + (++v) + ']><i></i><![endif]-->',
all[0]

Et à l'intérieur de l'opérateur virgule, les deux sont évalués bien que l'on aurait pu leur faire des déclarations séparées d'une manière ou d'une autre.

Sarfraz
la source
3
Cela aurait pu être une do- whileboucle.
Casey Chu
5

Il y a quelque chose d '«étrange» qui peut être fait en JavaScript en appelant indirectement une fonction en utilisant l'opérateur virgule.

Il y a une longue description ici: Appel de fonction indirecte 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 accéder à la variable globale car elle evalfonctionne différemment lorsqu'elle est appelée directement ou indirectement.

Jeremy J Starcher
la source
4

J'ai trouvé l'opérateur virgule le plus utile lors de l'écriture d'aides comme celle-ci.

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 par virgule communique l' intention - le code ne se soucie pas de ce que l'opérande de gauche évalue, alors que les alternatives peuvent avoir une autre raison d'être là. Cela facilite à son tour la compréhension et la refactorisation. Si le type de retour de fonction change un jour, le code ci-dessus ne sera pas affecté.

Naturellement, vous pouvez réaliser la même chose par d'autres moyens, mais pas aussi succinctement. Si || et && a trouvé une place dans l'usage courant, de même que l'opérateur virgule.

Lambros Symeonakis
la source
Similaire à ce que fait Ramda \ Lodash avec tap( ramdajs.com/docs/#tap ). Essentiellement, vous exécutez un effet secondaire, puis vous renvoyez la valeur initiale; très utile en programmation fonctionnelle :)
avalanche1
1

Un cas typique dans lequel je finis par l'utiliser est lors de l'analyse d'arguments facultatifs. Je pense que cela le rend à la fois plus lisible et plus concis afin 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();

  // ...
}
Riche Remer
la source
Bien que je ne le favorise pas moi-même, c'est le seul exemple que quelqu'un a donné que je pense qu'une personne pourrait dire que c'est mieux pour la lisibilité que la liste équivalente d'énoncés d'expressions individuelles sans que je pense qu'ils sont complètement fous.
Point
1

Disons que vous avez un tableau:

arr = [];

Lorsque vous êtes pushsur ce tableau, vous êtes rarement intéressé par pushla valeur de retour de 's, à savoir la nouvelle longueur du tableau, mais plutôt 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 à la virgule, puis utiliser le résultat - le tableau lui-même - pour un appel de méthode de tableau suivant, une sorte de chaînage:

(arr.push('bar'), arr.push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]
Dexygène
la source
-2

Un autre domaine dans lequel l'opérateur virgule peut être utilisé est l' obscurcissement du code .

Disons qu'un développeur écrit du code comme celui-ci:

var foo = 'bar';

Maintenant, elle décide de masquer le code. L'outil utilisé peut changer le code comme ceci:

var Z0b=(45,87)>(195,3)?'bar':(54,65)>(1,0)?'':'baz';// Z0b == 'bar'

Démo: http://jsfiddle.net/uvDuE/

Stéphan
la source
1
@gdoron Veuillez consulter cette réponse stackoverflow.com/a/17903036/363573 à propos de l'opérateur virgule en C ++. Vous remarquerez le commentaire de James Kanze sur l'obfuscation.
Stephan