Quando o operador vírgula é útil?

88

Eu li esta pergunta sobre o "operador de vírgula" em expressões ( ,) e os documentos MDN sobre ele, mas não consigo pensar em um cenário em que seja útil.

Então, quando o operador vírgula é útil?

gdoron está apoiando Monica
fonte
2
var i, j, k;vs var i; var j, var k?
Salman A
15
@SalmanA. Não tenho certeza se tem algo a ver com a ,operadora. Essa linha também é válida em C#, mas a ,operadora não existe lá.
gdoron está torcendo para Monica,
2
@SalmanA. Eu fiz. Não encontrei, me
esclareça
5
@SalmanA a ,nem sempre é o ,operador (e nunca é o ,operador em C #). Portanto, pode faltar um ,operador ao C # , embora ainda seja usado livremente ,como parte da sintaxe.
Seth Carnegie
8
Acho que as respostas aqui resumiram o fato de que o ,não é amplamente usado (e nem toda ocorrência de a ,é o operador vírgula) . Mas você pode pegá-lo e um Array emprestados para fazer uma troca de variável embutida sem criar uma variável temporária. Dado que você deseja trocar os valores de ae b, você pode fazer a = [b][b = a,0]. Isso coloca a corrente bno Array. A segunda []é a notação de acesso à propriedade. O índice acessado é 0, mas não antes de ser atribuído aa b, que agora é seguro, pois bé retido no Array. o ,nos permite fazer as expressões múltiplas no [].

Respostas:

128

O que se segue provavelmente não é muito útil, pois você não o escreve sozinho, mas um minificador pode reduzir o código usando o operador vírgula. Por exemplo:

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

se tornaria:

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

O ? :operador pode ser usado agora, já que o operador vírgula (até certo ponto) permite que duas instruções sejam escritas como uma única instrução.

Isso é útil porque permite uma compressão precisa (39 -> 24 bytes aqui).


Gostaria de enfatizar o fato de que a vírgula in nãovar a, b é o operador vírgula porque não existe em uma expressão . A vírgula tem um significado especial nas declarações . em uma expressão estaria se referindo às duas variáveis ​​e avaliada , o que não é o caso de .var a, bbvar a, b

pimvdb
fonte
3
O que você achou disso? Você leu em algum lugar? Está realmente sendo usado?
gdoron está torcendo para Monica em
16
Eu estava mexendo no Closure Compiler outro dia para ver o que ele realmente fazia, e percebi essa substituição.
pimvdb
2
Um uso semelhante, que acho útil em seu código, seria para atribuir várias variáveis ​​em uma instrução if embutida. Por exemplo: if (condition) var1 = val1, var2 = val2;eu pessoalmente acho que evitar colchetes sempre que possível torna o código mais legível.
Aidiakapi
2
Praticamente as únicas vezes que uso o operador vírgula são quando estou adicionando instruções de log às expressões (foo => (console.log ('foo', foo), foo)) ou se estou ficando muito esperto com a redução de iteratees . (pairs.reduce ((acc, [k, v]) => (acc [k] = v, acc), {}))
Joseph Sikorski
38

O operador vírgula permite que você coloque várias expressões em um lugar onde uma expressão é esperada. O valor resultante de várias expressões separadas por uma vírgula será o valor da última expressão separada por vírgula.

Eu pessoalmente não o uso com muita frequência porque não há muitas situações em que mais de uma expressão seja esperada e não há uma maneira menos confusa de escrever o código do que usando o operador vírgula. Uma possibilidade interessante é no final de um forloop, quando você deseja que mais de uma variável seja incrementada:

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

Aqui você vê que i++, j++pode ser colocado em um local onde uma expressão é permitida. Nesse caso específico, as expressões múltiplas são usadas para efeitos colaterais, portanto, não importa se as expressões compostas assumem o valor da última, mas há outros casos em que isso pode realmente importar.

jfriend00
fonte
38

O Operador de vírgula é frequentemente útil ao escrever código funcional em Javascript.

Considere este código que escrevi para um SPA um tempo atrás, que tinha algo como o seguinte

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

Este era um cenário bastante complexo, mas do mundo real. Tenha paciência enquanto eu explico o que está acontecendo e, no processo, explique o caso para o Operador de vírgula.


Isso usa o encadeamento de Underscore para

  1. Desmontar todas as opções passadas para esta função usando o pairs que se transformará { a: 1, b: 2}em[['a', 1], ['b', 2]]

  2. Essa matriz de pares de propriedades é filtrada por aqueles que são considerados "ações" no sistema.

  3. Em seguida, o segundo índice na matriz é substituído por uma função que retorna uma promessa que representa essa ação (usando map)

  4. Finalmente, a chamada para reducemesclará cada "array de propriedade" ( ['a', 1]) de volta em um objeto final.

O resultado final é uma versão transformada do optionsargumento, que contém apenas as chaves apropriadas e cujos valores são consumíveis pela função de chamada.


Olhando apenas

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

Você pode ver que a função de redução começa com um objeto de estado vazio,, statee para cada par que representa uma chave e um valor, a função retorna o mesmo stateobjeto após adicionar uma propriedade ao objeto correspondente ao par chave / valor. Por causa da sintaxe da função de seta do ECMAScript 2015, o corpo da função é uma expressão e, como resultado, o Operador de vírgula permite uma função de "iteração" concisa e útil .

Pessoalmente, encontrei vários casos ao escrever Javascript em um estilo mais funcional com ECMAScript 2015 + Funções de seta. Dito isso, antes de encontrar funções de seta (como no momento em que escrevi a pergunta), nunca usei o operador vírgula de forma deliberada.

Syynth
fonte
2
Esta é a única resposta realmente útil em relação a como / quando um programador pode usar o operador vírgula. Muito útil, especialmente emreduce
hgoebl
1
Com ES6 +, esta deve ser a resposta aceita.
Ardee Aram
Resposta de Nice, mas se eu puder sugerir algo um pouco mais legível: .reduce((state, [key, value]) => (state[key] = value, state), {}). E eu percebo que isso vai contra o propósito da resposta, mas .reduce((state, [key, value]) => Object.assign(state, { [key]: value }), {})eliminaria totalmente a necessidade do operador vírgula.
Patrick Roberts
Embora Object.assign provavelmente seja mais idiomático hoje em dia, ou mesmo apenas o operador de propagação, não tenho certeza se eles eram amplamente usados ​​na época. Também gostaria de salientar que, embora o operador vírgula seja um pouco mais obscuro, ele pode gerar muito menos lixo em situações em que o conjunto de dados é muito grande. A desestruturação certamente ajudaria na legibilidade!
Syynth
18

Outro uso do operador vírgula é ocultar resultados indesejados no repl ou no console, apenas por conveniência.

Por exemplo, se você avaliar myVariable = aWholeLotOfTextno repl ou console, ele imprimirá todos os dados que você acabou de atribuir. Isso pode ser páginas e páginas, e se você preferir não ver, você pode avaliar myVariable = aWholeLotOfText, 'done', e o repl / console imprimirá apenas 'concluído'.

Oriel aponta corretamente que funções toString()ou customizadas get()podem até tornar isso útil.

Julian de Bhal
fonte
1
Ha, ideia muito boa! (Finalmente, uma resposta que realmente responde à pergunta, ao contrário de quase todas as respostas {e 3 respostas excluídas que você precisa de 20K de reputação para ver ...})
gdoron está apoiando Monica de
1
Se o valor atribuído for um objeto, o console pode tentar exibi-lo de maneira adequada. Para fazer isso, ele pode, por exemplo, chamar getters, que podem alterar o estado do objeto. Usar uma vírgula pode evitar isso.
Oriol
@Oriol - Legal! Você está totalmente correto. De alguma forma, ser potencialmente útil parece um pouco decepcionante :)
Julian de Bhal
13

O operador vírgula não é específico do JavaScript, ele está disponível em outras linguagens como C e C ++ . Como um operador binário, isso é útil quando o primeiro operando, que geralmente é uma expressão, tem o efeito colateral desejado exigido pelo segundo operando. Um exemplo da wikipedia:

i = a += 2, a + b;

Obviamente, você pode escrever duas linhas de código diferentes, mas usar vírgulas é outra opção e às vezes mais legível.

taskinoor
fonte
1
Pense nisso como uma alternativa, embora a definição de bom possa variar de pessoa para pessoa. No entanto, não consigo encontrar nenhum exemplo em que você DEVE usar vírgula. Outra coisa semelhante é ternário?: Operador. Isso sempre pode ser substituído por if-else, mas às vezes?: Torna o código mais legível do que if-else. O mesmo conceito vale para a vírgula.
taskinoor
BTW, não estou considerando o uso de vírgula na declaração de variáveis ​​ou inicializar várias variáveis ​​em loop. Nesses casos, a vírgula é melhor.
taskinoor
2
isso parece confuso f ... wtf.
Timmerz de
6

Eu discordaria de Flanagan e diria que a vírgula é realmente útil e permite escrever um código mais legível e elegante, especialmente quando você sabe o que está fazendo:

Aqui está o artigo bastante detalhado sobre o uso de vírgulas:

Vários exemplos de lá para a prova de demonstração:

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

Um gerador 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

Encontre o primeiro elemento pai, análogo à .parent()função 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
fonte
7
Não tenho certeza se eu diria que isso é mais legível, mas certamente é bem
bacana
1
Você não precisa da vírgula no loop while no último exemplo, while ((el = el.parentNode) && (el.tagName != tagName.toUpperCase()))seria ótimo nesse contexto.
PitaJ
5

Não encontrei outro uso prático disso, mas aqui está um cenário em que James Padolsey usa muito bem essa técnica para detecção de IE em um loop 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;

}());

Essas duas linhas devem ser executadas:

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

E dentro do operador vírgula, ambos são avaliados, embora alguém pudesse tê-los feito instruções separadas de alguma forma.

Sarfraz
fonte
3
Este poderia ter sido um do- whileloop.
Casey Chu
5

Existe algo "estranho" que pode ser feito em JavaScript chamando uma função indiretamente usando o operador vírgula.

Há uma longa descrição aqui: Chamada de função indireta em JavaScript

Usando esta sintaxe:

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

Você pode obter acesso à variável global porque evalfunciona de maneira diferente quando chamado diretamente vs chamado indiretamente.

Jeremy J Starcher
fonte
4

Achei o operador vírgula mais útil ao escrever auxiliares como este.

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

Você pode substituir a vírgula por um || ou &&, mas então você precisa saber o que a função retorna.

Mais importante do que isso, o separador de vírgula comunica a intenção - o código não se importa com o que o operando esquerdo avalia, enquanto as alternativas podem ter outro motivo para estar lá. Isso, por sua vez, torna mais fácil entender e refatorar. Se o tipo de retorno da função mudar, o código acima não será afetado.

Naturalmente, você pode conseguir o mesmo de outras maneiras, mas não de forma tão sucinta. Se || e && encontrou um lugar de uso comum, assim também pode o operador vírgula.

Lambros Symeonakis
fonte
Semelhante ao que Ramda \ Lodash faz com tap( ramdajs.com/docs/#tap ). Essencialmente, você está executando um efeito colateral e, em seguida, retornando o valor inicial; muito útil na programação funcional :)
avalanche1
1

Um caso típico que acabo usando é durante a análise opcional do argumento. Acho que o torna mais legível e mais conciso para que a análise do argumento não domine o corpo da função.

/**
 * @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
fonte
Embora eu não goste disso, este é o único exemplo dado por alguém que eu acho que uma pessoa poderia dizer que é melhor para facilitar a leitura do que a lista equivalente de declarações de expressão individuais sem que eu ache que elas são completamente insanas.
Ponto
1

Digamos que você tenha uma matriz:

arr = [];

Quando você pushentra nessa matriz, raramente está interessado no pushvalor de retorno de, ou seja, o novo comprimento da matriz, mas sim na própria matriz:

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

Usando o operador vírgula, podemos enviar para a matriz, especificar a matriz como o último operando a vírgula e, em seguida, usar o resultado - a própria matriz - para uma chamada de método de matriz subsequente, uma espécie de encadeamento:

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

Outra área onde o operador vírgula pode ser usado é a Ofuscação de código .

Digamos que um desenvolvedor escreva algum código como este:

var foo = 'bar';

Agora, ela decide ofuscar o código. A ferramenta usada pode alterar o código assim:

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

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

Stephan
fonte
1
@gdoron Por favor, dê uma olhada nesta resposta stackoverflow.com/a/17903036/363573 sobre o Operador de vírgula em C ++. Você notará o comentário de James Kanze sobre a ofuscação.
Stephan de