¿Cuándo es útil el operador de coma?

88

Leí esta pregunta sobre el "operador de coma" en las expresiones ( ,) y los documentos de MDN al respecto, pero no puedo pensar en un escenario en el que sea útil.

Entonces, ¿cuándo es útil el operador de coma?

gdoron está apoyando a Monica
fuente
2
var i, j, k;vs var i; var j, var k?
Salman A
15
@SalmanA. No estoy seguro de que tenga nada que ver con el ,operador. Esa línea también es válida C#, pero el ,operador no existe allí.
gdoron está apoyando a Monica
2
@SalmanA. Yo hice. No lo encontré, enlight me ...
gdoron está apoyando a Monica
5
@SalmanA a ,no siempre es el ,operador (y nunca es el ,operador en C #). Por lo tanto, C # puede carecer de un ,operador mientras se usa libremente ,como parte de la sintaxis.
Seth Carnegie
8
Creo que las respuestas aquí han resumido el hecho de que ,no se usa ampliamente (y no todas las apariciones de a ,es el operador de coma) . Pero puede pedirlo prestado y una matriz para realizar un intercambio de variables en línea sin crear una variable temporal. Dado que desea intercambiar los valores de ay b, puede hacerlo a = [b][b = a,0]. Esto coloca la corriente ben el Array. El segundo []es la notación de acceso a la propiedad. El índice visitada es 0, pero no antes de asignar aa b, que ahora es seguro ya que bes retenida en la matriz. el ,nos permite hacer las múltiples expresiones en el [].

Respuestas:

128

Lo siguiente probablemente no sea muy útil ya que no lo escribe usted mismo, pero un minificador puede reducir el código usando el operador de coma. Por ejemplo:

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

se convertiría:

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

El ? :operador se puede usar ahora, ya que el operador de coma (hasta cierto punto) permite escribir dos declaraciones como una sola declaración.

Esto es útil porque permite una compresión ordenada (39 -> 24 bytes aquí).


Me gustaría enfatizar el hecho de que la coma novar a, b es el operador de coma porque no existe dentro de una expresión . La coma tiene un significado especial en las declaraciones . en una expresión estaría refiriéndose a las dos variables y evaluar , lo cual no es el caso de .var a, bbvar a, b

pimvdb
fuente
3
¿Cómo pensaste en eso? ¿Lo leíste en algún lugar? ¿Realmente se está utilizando?
gdoron apoya a Monica el
16
El otro día estaba jugando con Closure Compiler para ver qué hace realmente, y noté esta sustitución.
pimvdb
2
Un uso similar que creo que es útil en su código sería para asignar múltiples variables en una declaración if en línea. Por ejemplo: if (condition) var1 = val1, var2 = val2;personalmente creo que evitar los corchetes siempre que sea posible hace que el código sea más legible.
Aidiakapi
2
Casi las únicas veces que uso el operador de coma es cuando agrego declaraciones de registro a expresiones (foo => (console.log ('foo', foo), foo)), o si me estoy volviendo demasiado inteligente con reducir iteraciones . (pares.reducir ((acc, [k, v]) => (acc [k] = v, acc), {}))
Joseph Sikorski
38

El operador de coma le permite colocar varias expresiones en un lugar donde se espera una expresión. El valor resultante de varias expresiones separadas por una coma será el valor de la última expresión separada por comas.

Personalmente, no lo uso muy a menudo porque no hay muchas situaciones en las que se espera más de una expresión y no hay una forma menos confusa de escribir el código que usar el operador de coma. Una posibilidad interesante es al final de un forciclo cuando desea que se incremente más de una 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
}

Aquí puede ver que i++, j++se puede poner en un lugar donde se permite una expresión. En este caso particular, las expresiones múltiples se utilizan para efectos secundarios, por lo que no importa que las expresiones compuestas adquieran el valor de la última, pero hay otros casos en los que eso podría realmente importar.

jfriend00
fuente
38

El operador de coma suele ser útil al escribir código funcional en Javascript.

Considere este código que escribí para un SPA hace un tiempo que tenía algo como lo siguiente

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

Este fue un escenario bastante complejo, pero del mundo real. Ténganme paciencia mientras les explico lo que está sucediendo y, en el proceso, defiendo el caso del operador de coma.


Esto usa el encadenamiento de Underscore para

  1. Desarma todas las opciones pasadas a esta función usando pairs que se convertirá { a: 1, b: 2}en[['a', 1], ['b', 2]]

  2. Esta matriz de pares de propiedades se filtra según cuáles se consideran "acciones" en el sistema.

  3. Luego, el segundo índice en la matriz se reemplaza con una función que devuelve una promesa que representa esa acción (usando map)

  4. Finalmente, la llamada a reducefusionará cada "matriz de propiedades" ( ['a', 1]) de nuevo en un objeto final.

El resultado final es una versión transformada del optionsargumento, que contiene solo las claves apropiadas y cuyos valores son consumibles por la función de llamada.


Mirando solo

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

Puede ver que la función de reducción comienza con un objeto de estado vacío state, y para cada par que representa una clave y un valor, la función devuelve el mismo stateobjeto después de agregar una propiedad al objeto correspondiente al par clave / valor. Debido a la sintaxis de la función de flecha de ECMAScript 2015, el cuerpo de la función es una expresión y, como resultado, el operador de coma permite una función "iteratee" concisa y útil .

Personalmente, me he encontrado con numerosos casos al escribir Javascript en un estilo más funcional con ECMAScript 2015 + Arrow Functions. Dicho esto, antes de encontrarme con las funciones de flecha (como en el momento de escribir la pregunta), nunca había usado el operador de coma de forma deliberada.

Sintetizador
fuente
2
Esta es la única respuesta realmente útil con respecto a cómo / cuándo puede un programador usar el operador de coma. Muy útil especialmente enreduce
hgoebl
1
Con ES6 +, esta debería ser la respuesta aceptada.
Ardee Aram
Respuesta agradable, pero si se me permite sugerir algo un poco más legible: .reduce((state, [key, value]) => (state[key] = value, state), {}). Y me doy cuenta de que esto frustra el propósito de la respuesta, pero .reduce((state, [key, value]) => Object.assign(state, { [key]: value }), {})eliminaría por completo la necesidad del operador de coma.
Patrick Roberts
Si bien Object.assign es probablemente más idiomático en estos días, o incluso solo el operador de propagación, no estoy seguro de que se usaran ampliamente en ese momento. También señalaría que si bien el operador de coma es un poco más oscuro, esto podría generar mucha menos basura en situaciones en las que el conjunto de datos es muy grande. Sin embargo, la desestructuración ayudaría a la legibilidad.
Syynth
18

Otro uso del operador de coma es ocultar los resultados que no le interesan en la respuesta o en la consola, simplemente por conveniencia.

Por ejemplo, si evalúa myVariable = aWholeLotOfTexten la réplica o la consola, imprimirá todos los datos que acaba de asignar. Estas pueden ser páginas y páginas, y si prefiere no verlas, puede evaluarlas myVariable = aWholeLotOfText, 'done', y la respuesta / consola simplemente imprimirá 'listo'.

Oriel señala correctamente que las funciones personalizadas toString()o get()incluso podrían hacer que esto sea útil.

Julian de Bhal
fuente
1
¡Muy bonita idea! (Finalmente una respuesta que realmente responde a la pregunta a diferencia de casi todas las respuestas {y 3 respuestas eliminadas que necesitas 20K de reputación para ver ...})
gdoron apoya a Monica
1
Si el valor asignado es un objeto, la consola puede intentar mostrarlo correctamente. Para hacer eso, por ejemplo, podría llamar a getters, lo que podría cambiar el estado del objeto. El uso de una coma puede evitar esto.
Oriol
@Oriol - ¡Bien! Estás totalmente en lo correcto. De alguna manera, ser potencialmente útil se siente un poco decepcionante :)
Julian de Bhal
13

El operador de coma no es específico de JavaScript, está disponible en otros lenguajes como C y C ++ . Como operador binario, esto es útil cuando el primer operando, que generalmente es una expresión, tiene el efecto secundario deseado requerido por el segundo operando. Un ejemplo de wikipedia:

i = a += 2, a + b;

Obviamente, puede escribir dos líneas de códigos diferentes, pero usar la coma es otra opción y, a veces, más legible.

taskinoor
fuente
1
Piense en esto como una alternativa, aunque la definición de bien puede variar de una persona a otra. Sin embargo, no puedo encontrar ningún ejemplo en el que DEBE usar una coma. Otra cosa similar es ternario?: Operador. Eso siempre puede ser reemplazado por if-else pero a veces?: Hace un código más legible que if-else. El mismo concepto se aplica también a la coma.
taskinoor
Por cierto, no estoy considerando el uso de comas en la declaración de variables o la inicialización de múltiples variables en bucle. En esos casos, la coma es mucho mejor.
taskinoor
2
esto parece confuso j ... wtf.
Timmerz
6

No estaría de acuerdo con Flanagan y diría que la coma es realmente útil y permite escribir código más legible y elegante, especialmente cuando sabes lo que estás haciendo:

Aquí está el artículo muy detallado sobre el uso de comas:

Varios ejemplos extraídos de allí para la prueba de demostración:

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

Un generador 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

Encuentre el primer elemento padre, análogo de la .parent()función 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">
chamán señor
fuente
7
No estoy seguro de si diría que algo de eso es más legible, pero ciertamente es bastante elegante, así que +1
Chris Marisic
1
No necesita la coma en el ciclo while en el último ejemplo, while ((el = el.parentNode) && (el.tagName != tagName.toUpperCase()))estaría bien en ese contexto.
PitaJ
5

No he encontrado un uso práctico aparte de eso, pero aquí hay un escenario en el que James Padolsey usa muy bien esta técnica para la detección de IE en 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;

}());

Estas dos líneas deben ejecutarse:

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

Y dentro del operador de coma, ambos se evalúan aunque uno podría haberlos hecho declaraciones separadas de alguna manera.

Sarfraz
fuente
3
Esto podría haber sido un do- whilebucle.
Casey Chu
5

Hay algo "extraño" que se puede hacer en JavaScript llamando a una función indirectamente usando el operador de coma.

Aquí hay una descripción larga: llamada de función indirecta en JavaScript

Usando esta sintaxis:

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

Puede obtener acceso a la variable global porque evalfunciona de manera diferente cuando se llama directamente o cuando se llama indirectamente.

Jeremy J Starcher
fuente
4

He encontrado que el operador de coma es más útil al escribir ayudantes como este.

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

Puedes reemplazar la coma con una || o &&, pero luego necesitaría saber qué devuelve la función.

Más importante que eso, el separador de coma comunica la intención : al código no le importa lo que evalúe el operando izquierdo, mientras que las alternativas pueden tener otra razón para estar allí. Esto, a su vez, hace que sea más fácil de entender y refactorizar. Si el tipo de retorno de la función cambia alguna vez, el código anterior no se verá afectado.

Naturalmente, puede lograr lo mismo de otras maneras, pero no tan sucintamente. Si || y && encontraron un lugar en el uso común, al igual que el operador de coma.

Lambros Symeonakis
fuente
Similar a lo que hace Ramda \ Lodash con tap( ramdajs.com/docs/#tap ). Básicamente, está ejecutando un efecto secundario y luego devolviendo el valor inicial; muy útil en programación funcional :)
avalanche1
1

Un caso típico en el que termino usándolo es durante el análisis de argumentos opcional. Creo que lo hace más legible y más conciso para que el análisis de argumentos no domine el cuerpo de la función.

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

  // ...
}
Rich Remer
fuente
Aunque yo no lo favorezco, este es el único ejemplo que alguien ha dado que creo que una persona podría decir que es mejor para la legibilidad que la lista equivalente de declaraciones de expresiones individuales sin que yo piense que están completamente locas.
Punto
1

Digamos que tienes una matriz:

arr = [];

Cuando ingresa pusha esa matriz, rara vez está interesado en pushel valor de retorno, es decir, la nueva longitud de la matriz, sino más bien la matriz en sí:

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

Usando el operador de coma, podemos presionar sobre la matriz, especificar la matriz como el último operando de la coma y luego usar el resultado, la matriz en sí, para una llamada posterior al método de matriz, una especie de encadenamiento:

(arr.push('bar'), arr.push('baz'), arr).sort(); // [ 'bar', 'baz', 'foo' ]
Dexygen
fuente
-2

Otra área donde se puede utilizar el operador de coma es la ofuscación de código .

Digamos que un desarrollador escribe un código como este:

var foo = 'bar';

Ahora, decide ofuscar el código. La herramienta utilizada puede cambiar el código de esta manera:

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

Demostración: http://jsfiddle.net/uvDuE/

Stephan
fuente
1
@gdoron Por favor, eche un vistazo a esta respuesta stackoverflow.com/a/17903036/363573 sobre el operador de coma en C ++. Notarás el comentario de James Kanze sobre la ofuscación.
Stephan