Quando è utile l'operatore virgola?


88

Ho letto questa domanda sull '"operatore virgola" in expression ( ,) e nei documenti MDN a riguardo, ma non riesco a pensare a uno scenario in cui sia utile.

Quindi, quando è utile l'operatore virgola?


2
var i, j, k;vs var i; var j, var k?
Salman A

15
@SalmanA. Non sono sicuro che abbia qualcosa a che fare con l' ,operatore. Quella riga è valida C#anche in, ma l' ,operatore non esiste lì.
gdoron supporta Monica

2
@SalmanA. L'ho fatto. Non l'ho trovato,
illuminami

5
@SalmanA a ,non è sempre l' ,operatore (e non è mai l' ,operatore in C #). Quindi C # può mancare di un ,operatore mentre lo usa ancora liberamente ,come parte della sintassi.
Seth Carnegie

8
Penso che le risposte qui abbiano riassunto il fatto che ,non è ampiamente utilizzato (e non tutte le occorrenze di a ,sono l'operatore virgola) . Ma puoi prenderlo in prestito e un Array per eseguire uno scambio di variabili in linea senza creare una variabile temporanea. Dato che vuoi scambiare i valori di ae b, puoi farlo a = [b][b = a,0]. Questo pone la corrente bnell'Array. Il secondo []è la notazione di accesso alla proprietà. L'indice a cui si accede è 0, ma non prima dell'assegnazione aa b, che ora è sicuro poiché bviene conservato nell'Array. la ,ci permette di fare le molteplici espressioni nella [].

Risposte:


128

Quanto segue probabilmente non è molto utile in quanto non lo scrivi da solo, ma un minifier può ridurre il codice usando l'operatore virgola. Per esempio:

if(x){foo();return bar()}else{return 1}

potrebbe diventare:

return x?(foo(),bar()):1

L' ? :operatore può essere utilizzato ora, poiché l'operatore virgola (in una certa misura) consente di scrivere due istruzioni come un'unica istruzione.

Ciò è utile in quanto consente una compressione accurata (39 -> 24 byte qui).


Vorrei sottolineare il fatto che la virgola in nonvar a, b è l'operatore virgola perché non esiste all'interno di un'espressione . La virgola ha un significato speciale nelle dichiarazioni . in un'espressione farebbe riferimento alle due variabili e restituirebbe , il che non è il caso .var a, bbvar a, b


3
Come ci hai pensato? L'hai letto da qualche parte? Viene davvero utilizzato?
gdoron supporta Monica il

16
L' altro giorno stavo solo scherzando con Closure Compiler per vedere cosa fa effettivamente, e ho notato questa sostituzione.
pimvdb

2
Un uso simile che penso sia utile nel tuo codice sarebbe per assegnare più variabili in un'istruzione if inline. Ad esempio: if (condition) var1 = val1, var2 = val2;personalmente penso che evitare le parentesi ove possibile renda il codice più leggibile.
Aidiakapi

2
Le uniche volte in cui uso l'operatore virgola sono quando aggiungo istruzioni di log alle espressioni (foo => (console.log ('foo', foo), foo)), o se sto diventando troppo intelligente con riduci iterate . (pair.reduce ((acc, [k, v]) => (acc [k] = v, acc), {}))
Joseph Sikorski

38

L'operatore virgola consente di inserire più espressioni in un punto in cui è prevista un'espressione. Il valore risultante di più espressioni separate da una virgola sarà il valore dell'ultima espressione separata da virgole.

Personalmente non lo uso molto spesso perché non ci sono molte situazioni in cui è prevista più di un'espressione e non c'è un modo meno confuso per scrivere il codice rispetto all'utilizzo dell'operatore virgola. Una possibilità interessante è alla fine di un forciclo quando si desidera che più di una variabile venga incrementata:

// 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
}

Qui vedi che i++, j++può essere messo in un luogo in cui è consentita un'espressione. In questo caso particolare, le espressioni multiple vengono utilizzate per gli effetti collaterali, quindi non importa che le espressioni composte assumano il valore dell'ultima, ma ci sono altri casi in cui ciò potrebbe effettivamente avere importanza.


38

L'operatore virgola è spesso utile quando si scrive codice funzionale in Javascript.

Considera questo codice che ho scritto per una SPA qualche tempo fa che aveva qualcosa di simile al seguente

const actions = _.chain(options)
                 .pairs() // 1
                 .filter(selectActions) // 2
                 .map(createActionPromise) // 3
                 .reduce((state, pair) => (state[pair[0]] = pair[1], state), {}) // 4
                 .value();

Questo era uno scenario abbastanza complesso, ma reale. Abbi pazienza mentre spiego cosa sta succedendo, e nel processo difendi l'operatore virgola.


Questo utilizza Underscore 's concatenamento a

  1. Smonta tutte le opzioni passate a questa funzione usando pairs che si trasformerà { a: 1, b: 2}in[['a', 1], ['b', 2]]

  2. Questo array di coppie di proprietà viene filtrato in base a quelle che sono considerate "azioni" nel sistema.

  3. Quindi il secondo indice nell'array viene sostituito con una funzione che restituisce una promessa che rappresenta quell'azione (utilizzando map)

  4. Infine la chiamata a reduceunirà ogni "array di proprietà" ( ['a', 1]) in un oggetto finale.

Il risultato finale è una versione trasformata optionsdell'argomento, che contiene solo le chiavi appropriate ei cui valori possono essere utilizzati dalla funzione chiamante.


Guardando solo

.reduce((state, pair) => (state[pair[0]] = pair[1], state), {})

Puoi vedere che la funzione di riduzione inizia con un oggetto di stato vuoto statee, per ogni coppia che rappresenta una chiave e un valore, la funzione restituisce lo stesso stateoggetto dopo aver aggiunto una proprietà all'oggetto corrispondente alla coppia chiave / valore. A causa della sintassi della funzione freccia di ECMAScript 2015, il corpo della funzione è un'espressione e, di conseguenza, l'operatore virgola consente una funzione di "iterazione" concisa e utile .

Personalmente mi sono imbattuto in numerosi casi mentre scrivevo Javascript in uno stile più funzionale con ECMAScript 2015 + Arrow Functions. Detto questo, prima di incontrare le funzioni freccia (come al momento della stesura della domanda), non avevo mai usato l'operatore virgola in modo deliberato.


2
Questa è l'unica risposta veramente utile riguardo a come / quando un programmatore può usare l'operatore virgola. Molto utile soprattutto inreduce
hgoebl

Bella risposta, ma se posso suggerire qualcosa di un po ' più leggibile: .reduce((state, [key, value]) => (state[key] = value, state), {}). E mi rendo conto che questo vanifica lo scopo della risposta ma .reduce((state, [key, value]) => Object.assign(state, { [key]: value }), {})eliminerebbe completamente la necessità dell'operatore virgola.
Patrick Roberts

Mentre Object.assign è probabilmente più idiomatico in questi giorni, o anche solo l'operatore di diffusione, non sono sicuro che fossero ampiamente utilizzati in quel momento. Vorrei anche sottolineare che mentre l'operatore virgola è un po 'più oscuro, questo potrebbe generare molta meno spazzatura in situazioni in cui l'insieme di dati è molto grande. La destrutturazione però aiuterebbe sicuramente la leggibilità!
Syynth

18

Un altro utilizzo per l'operatore virgola è nascondere i risultati che non ti interessano nella sostituzione o nella console, per puro vantaggio.

Ad esempio, se si valuta myVariable = aWholeLotOfTextnella sostituzione o nella console, verranno stampati tutti i dati appena assegnati. Potrebbe trattarsi di pagine e pagine, e se preferisci non vederlo, puoi invece valutare myVariable = aWholeLotOfText, 'done', e il repl / console stamperà semplicemente 'done'.

Oriel sottolinea correttamente che funzioni personalizzate toString()o get()potrebbero persino renderlo utile.


1
Ah, un'idea molto bella! (Finalmente una risposta che risponde effettivamente alla domanda a differenza di quasi tutte le risposte {e 3 risposte eliminate che ti servono 20.000 reputazione per vedere ...})
gdoron supporta Monica

1
Se il valore assegnato è un oggetto, la console potrebbe tentare di visualizzarlo correttamente. Per fare ciò, ad esempio, potrebbe chiamare getter, che potrebbe cambiare lo stato dell'oggetto. L'uso di una virgola può impedirlo.
Oriol

@Oriol - Bello! Hai completamente ragione. In qualche modo questo essere potenzialmente utile sembra solo un po 'deludente :)
Julian de Bhal

13

L'operatore virgola non è specifico di JavaScript, è disponibile in altri linguaggi come C e C ++ . Come operatore binario questo è utile quando il primo operando, che generalmente è un'espressione, ha l'effetto collaterale desiderato richiesto dal secondo operando. Un esempio da wikipedia:

i = a += 2, a + b;

Ovviamente puoi scrivere due diverse righe di codice, ma usare la virgola è un'altra opzione ea volte più leggibile.


1
Pensala come un'alternativa, anche se la definizione di bene potrebbe variare da persona a persona. Tuttavia, non riesco a trovare alcun esempio in cui DEVI usare la virgola. Un'altra cosa simile è l'operatore ternario?:. Questo può sempre essere sostituito da if-else ma a volte?: Rende il codice più leggibile di if-else. Lo stesso concetto vale anche per la virgola.
taskinoor

BTW, non sto considerando l'uso della virgola nella dichiarazione delle variabili o l'inizializzazione di più variabili in ciclo. In questi casi la virgola è per lo più meglio.
taskinoor

2
questo sembra confuso af ... wtf.
Timmerz

6

Non sono d'accordo con Flanagan e direi che la virgola è davvero utile e consente di scrivere codice più leggibile ed elegante, soprattutto quando sai cosa stai facendo:

Ecco l' articolo molto dettagliato sull'uso della virgola:

Diversi esempi da lì per la prova della dimostrazione:

function renderCurve() {
  for(var a = 1, b = 10; a*b; a++, b--) {
    console.log(new Array(a*b).join('*'));
  }
}

Un generatore di 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

Trova il primo elemento genitore, analogo della .parent()funzione 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">

7
Non sono sicuro se direi che è più leggibile, ma è sicuramente piuttosto elegante, quindi +1
Chris Marisic

1
Non hai bisogno della virgola nel ciclo while nell'ultimo esempio, while ((el = el.parentNode) && (el.tagName != tagName.toUpperCase()))andrebbe bene in quel contesto.
PitaJ

5

Non ne ho trovato un uso pratico diverso da quello, ma ecco uno scenario in cui James Padolsey utilizza piacevolmente questa tecnica per il rilevamento di IE in un ciclo 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;

}());

Queste due righe devono essere eseguite:

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

E all'interno dell'operatore virgola, entrambi vengono valutati anche se in qualche modo si sarebbero potuti rendere dichiarazioni separate.


3
Questo avrebbe potuto essere un do- whileloop.
Casey Chu

5

C'è qualcosa di "strano" che può essere fatto in JavaScript chiamando una funzione indirettamente utilizzando l'operatore virgola.

C'è una lunga descrizione qui: Chiamata indiretta di funzione in JavaScript

Utilizzando questa sintassi:

(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);
  
  }());

È possibile accedere alla variabile globale perché evalfunziona in modo diverso quando viene chiamata direttamente rispetto a quando viene chiamata indirettamente.


4

Ho trovato l'operatore virgola più utile quando si scrivono helper come questo.

const stopPropagation = event => (event.stopPropagation(), event);
const preventDefault = event => (event.preventDefault(), event);
const both = compose(stopPropagation, preventDefault);

Puoi sostituire la virgola con un || o &&, ma poi dovresti sapere cosa restituisce la funzione.

Ancora più importante, il separatore virgola comunica l' intento : al codice non interessa cosa valuta l'operando di sinistra, mentre le alternative possono avere un'altra ragione per essere lì. Questo a sua volta rende più facile la comprensione e il refactoring. Se il tipo di ritorno della funzione cambia mai, il codice precedente non ne risentirà.

Naturalmente puoi ottenere la stessa cosa in altri modi, ma non così succintamente. Se || e && hanno trovato un posto di uso comune, così come l'operatore virgola.


Simile a quello che fa Ramda \ Lodash con tap( ramdajs.com/docs/#tap ). In sostanza, stai eseguendo un effetto collaterale e quindi restituendo il valore iniziale; molto utile nella programmazione funzionale :)
avalanche1

1

Un caso tipico in cui finisco per usarlo è durante l'analisi degli argomenti opzionali. Penso che lo renda più leggibile e più conciso in modo che l'analisi dell'argomento non domini il corpo della funzione.

/**
 * @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();

  // ...
}

Sebbene io stesso non lo favorisca, questo è l'unico esempio fornito da qualcuno che penso che una persona possa dire che è migliore per la leggibilità rispetto all'elenco equivalente di dichiarazioni di espressioni individuali senza che io pensi che siano completamente pazze.
Punto

1

Diciamo che hai un array:

arr = [];

Quando ti trovi pushsu quell'array, raramente sei interessato al pushvalore di ritorno di, vale a dire la nuova lunghezza dell'array, ma piuttosto l'array stesso:

arr.push('foo')  // ['foo'] seems more interesting than 1

Utilizzando l'operatore virgola, possiamo eseguire il push sull'array, specificare l'array come ultimo operando della virgola e quindi utilizzare il risultato - l'array stesso - per una successiva chiamata al metodo dell'array, una sorta di concatenamento:

(arr.push('bar'), arr.push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]

-2

Un'altra area in cui è possibile utilizzare l'operatore virgola è l' offuscamento del codice .

Diciamo che uno sviluppatore scrive un codice come questo:

var foo = 'bar';

Ora decide di offuscare il codice. Lo strumento utilizzato può modificare il codice in questo modo:

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

Demo: http://jsfiddle.net/uvDuE/


1
@gdoron prega di dare un'occhiata a questa risposta stackoverflow.com/a/17903036/363573 circa l'operatore virgola in C ++. Noterai il commento di James Kanze sull'offuscamento.
Stephan
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.