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?
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?
,
operatore. Quella riga è valida C#
anche in, ma l' ,
operatore non esiste lì.
,
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.
,
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 a
e b
, puoi farlo a = [b][b = a,0]
. Questo pone la corrente b
nell'Array. Il secondo []
è la notazione di accesso alla proprietà. L'indice a cui si accede è 0
, ma non prima dell'assegnazione a
a b
, che ora è sicuro poiché b
viene conservato nell'Array. la ,
ci permette di fare le molteplici espressioni nella []
.
Risposte:
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, b
b
var a, b
if (condition) var1 = val1, var2 = val2;
personalmente penso che evitare le parentesi ove possibile renda il codice più leggibile.
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 for
ciclo 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.
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.
Smonta tutte le opzioni passate a questa funzione usando pairs
che si trasformerà { a: 1, b: 2}
in[['a', 1], ['b', 2]]
Questo array di coppie di proprietà viene filtrato in base a quelle che sono considerate "azioni" nel sistema.
Quindi il secondo indice nell'array viene sostituito con una funzione che restituisce una promessa che rappresenta quell'azione (utilizzando map
)
Infine la chiamata a reduce
unirà ogni "array di proprietà" ( ['a', 1]
) in un oggetto finale.
Il risultato finale è una versione trasformata options
dell'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 state
e, per ogni coppia che rappresenta una chiave e un valore, la funzione restituisce lo stesso state
oggetto 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.
reduce
.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.
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 = aWholeLotOfText
nella 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.
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.
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">
while ((el = el.parentNode) && (el.tagName != tagName.toUpperCase()))
andrebbe bene in quel contesto.
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.
do
- while
loop.
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é eval
funziona in modo diverso quando viene chiamata direttamente rispetto a quando viene chiamata indirettamente.
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.
tap
( ramdajs.com/docs/#tap ). In sostanza, stai eseguendo un effetto collaterale e quindi restituendo il valore iniziale; molto utile nella programmazione funzionale :)
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();
// ...
}
Diciamo che hai un array:
arr = [];
Quando ti trovi push
su quell'array, raramente sei interessato al push
valore 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' ]
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'
var i, j, k;
vsvar i; var j, var k
?